1
0
mirror of https://github.com/PanJiaChen/vue-element-admin.git synced 2025-08-07 18:25:45 +08:00

Feature/#doc add documentation (#798)

* Add support to x vversion from npm

* Add support to x vversion from npm

* Add support to x vversion from npm

* Add documentation for current repository
This commit is contained in:
Yamel Senih 2021-04-29 12:23:48 -04:00 committed by GitHub
parent 925cf6d679
commit f45a57178a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
139 changed files with 11506 additions and 1 deletions

53
.github/workflows/documentation.yml vendored Normal file
View File

@ -0,0 +1,53 @@
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
# For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages
# This file was contributed by Carlos Parada and Yamel Senih from ERP Consultores y Asociados, C.A
name: Build Project
# Controls when the action will run.
on:
# Triggers the workflow on push or pull request events but only for the master branch
push:
branches: [ master, develop ]
pull_request:
branches: [ master, develop ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14.x]
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: set -e
- run: |
cd docs
npm i
npm run build
- run: |
cd docs
git clone https://github.com/adempiere/adempiere-vue.git --branch gh-pages --single-branch gh-pages
cp -r .vuepress/dist/* gh-pages/
cd gh-pages
touch .nojekyll
git init
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git add .
git commit -m "Update documentation" -a || true
- uses: ad-m/github-push-action@master
with:
branch: gh-pages
directory: docs/gh-pages
github_token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -11,11 +11,15 @@ on:
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [10.x, 12.x, 13.x, 14.x]
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 14
node-version: ${{ matrix.node-version }}
- run: npm i
- run: npm test
- run: npm run build:prod --if-present

View File

@ -0,0 +1,20 @@
<template>
<a
href="https://coding.net/?utm_source=panjiachen"
target="_blank"
@click="clickCoding('readme')"
style="display: block;"
>
<img src="https://adempiere-vue.gitee.io/gitee-cdn//72dd4d11-8958-4690-8adb-cfdd3a3a30cc.png">
</a>
</template>
<script>
export default {
methods: {
clickCoding(tag) {
ga('send', 'click', 'e.coding', 'Action', tag)
}
}
}
</script>

268
docs/.vuepress/config.js Normal file
View File

@ -0,0 +1,268 @@
var nav = require('./nav.js')
var { EcosystemNav, ComponentNav, BackendNav } = nav
var utils = require('./utils.js')
var { genNav, getComponentSidebar, deepClone } = utils
module.exports = {
title: 'adempiere-vue',
description: 'The new UI for ADempiere ERP',
base: '/adempiere-vue/',
head: [
[
'link',
{
rel: 'icon',
href: '/favicon.ico'
}
]
],
themeConfig: {
repo: 'adempiere/adempiere-vue',
docsRepo: 'adempiere/adempiere-vue',
docsDir: 'docs',
editLinks: true,
sidebarDepth: 3,
algolia: {
apiKey: 'ffce0083d0830de5f562c045a481410b',
indexName: 'vue_element_admin'
},
locales: {
'/': {
label: 'English',
selectText: 'Languages',
editLinkText: 'Edit this page on GitHub',
nav: [
{
text: 'Guide',
link: '/guide/'
},
{
text: 'Features',
items: genNav([...BackendNav, ...deepClone(ComponentNav)], 'EN')
},
{
text: 'Ecosystem',
items: genNav(deepClone(EcosystemNav), 'EN')
},
{
text: 'Donate',
link: '/donate/'
},
{
text: '中文站点(gitee)',
link: 'https://adempiere-vue.gitee.io/adempiere-vue/zh/'
}
],
sidebar: {
'/guide/': [
{
title: 'Essentials',
collapsable: false,
children: genEssentialsSidebar()
},
{
title: 'Advanced',
collapsable: false,
children: genAdvancedSidebar()
},
{
title: 'Other',
collapsable: false,
children: [
'/guide/other/gitter.md',
'/guide/other/release-notes.md'
]
}
],
'/feature/component/': getComponentSidebar(
deepClone(ComponentNav),
'EN'
),
'/feature/script/': [
'/feature/script/svgo.md',
'/feature/script/new.md'
]
}
},
'/es/': {
label: 'Español',
selectText: 'Idiomas',
editLinkText: 'Editar esta página en GitHub',
nav: [
{
text: 'Guía',
link: '/es/guide/'
},
{
text: 'Características',
items: genNav([...BackendNav, ...deepClone(ComponentNav)], 'ES')
},
{
text: 'Ecosistema',
items: genNav(deepClone(EcosystemNav), 'ES')
},
{
text: 'Donar',
link: '/es/donate/'
}
],
sidebar: {
'/es/guide/': [
{
title: 'Esenciales',
collapsable: false,
children: genEssentialsSidebar('/es')
},
{
title: 'Avanzado',
collapsable: false,
children: genAdvancedSidebar('/es')
},
{
title: 'Otro',
collapsable: false,
children: [
'/es/guide/other/gitter.md',
'/es/guide/other/release-notes.md'
]
}
],
'/es/feature/component/': getComponentSidebar(
deepClone(ComponentNav),
'ES'
),
'/es/feature/script/': [
'/es/feature/script/svgo.md',
'/es/feature/script/new.md'
]
}
},
'/zh/': {
label: '简体中文',
selectText: '选择语言',
editLinkText: '在 GitHub 上编辑此页',
nav: [
{
text: '指南',
link: '/zh/guide/'
},
{
text: '功能',
items: genNav([...BackendNav, ...deepClone(ComponentNav)], 'ZH')
},
{
text: '生态系统',
items: genNav(deepClone(EcosystemNav), 'ZH')
},
{
text: '捐赠',
link: '/zh/donate/'
},
{
text: '中文站点(gitee)',
link: 'https://adempiere-vue.gitee.io/adempiere-vue/zh/'
},
{
text: '招聘',
link: '/zh/job/'
}
],
sidebar: {
'/zh/guide/': [
{
title: '基础',
collapsable: false,
children: genEssentialsSidebar('/zh')
},
{
title: '进阶',
collapsable: false,
children: genAdvancedSidebar('/zh')
},
{
title: '其它',
collapsable: false,
children: [
'/zh/guide/other/faq.md',
'/zh/guide/other/release-notes.md'
]
}
],
'/zh/feature/component/': getComponentSidebar(
deepClone(ComponentNav),
'ZH'
),
'/zh/feature/script/': [
'/zh/feature/script/svgo.md',
'/zh/feature/script/new.md'
]
}
}
}
},
locales: {
'/': {
lang: 'en-US',
description: 'The new UI for ADempiere ERP'
},
'/zh/': {
lang: 'zh-CN',
description: 'The new UI for ADempiere ERP'
},
'/es/': {
lang: 'es-ES',
description:
'La nueva UI para ADempiere ERP, tome su tiempo para ver estamaravillosa interfaz adaptada a los requerimientos de su negocio'
}
},
configureWebpack: {
resolve: {
alias: {
'@public': './public'
}
}
},
ga: 'UA-109340118-1'
}
function genEssentialsSidebar(type = '') {
const mapArr = [
'/guide/',
'/guide/essentials/layout.md',
'/guide/essentials/router-and-nav.md',
'/guide/essentials/permission.md',
'/guide/essentials/tags-view.md',
'/guide/essentials/new-page.md',
'/guide/essentials/style.md',
'/guide/essentials/server.md',
'/guide/essentials/mock-api.md',
'/guide/essentials/import.md',
'/guide/essentials/deploy.md',
'/guide/essentials/env.md'
]
return mapArr.map(i => {
return type + i
})
}
function genAdvancedSidebar(type = '') {
const mapArr = [
'/guide/advanced/cors.md',
'/guide/advanced/eslint.md',
'/guide/advanced/git-hook.md',
'/guide/advanced/style-guide.md',
'/guide/advanced/lazy-loading.md',
'/guide/advanced/chart.md',
'/guide/advanced/icon.md',
'/guide/advanced/cdn.md',
'/guide/advanced/theme.md',
'/guide/advanced/i18n.md',
'/guide/advanced/error.md',
'/guide/advanced/webpack.md',
'/guide/advanced/sass.md'
]
return mapArr.map(i => {
return type + i
})
}

172
docs/.vuepress/nav.js Normal file
View File

@ -0,0 +1,172 @@
var EcosystemNav = [
{
textEN: 'Repositories',
textES: 'Repositorios',
textZH: '项目',
items: [
{
text: 'adempiere-vue',
link: 'https://github.com/adempiere/adempiere-vue'
},
{
text: 'vue-admin-template',
link: 'https://github.com/adempiere/vue-admin-template'
},
{
text: 'electron-vue-admin',
link: 'https://github.com/PanJiaChen/electron-vue-admin'
},
{
text: 'vue-typescript-admin-template',
link: 'https://github.com/Armour/vue-typescript-admin-template'
},
{
text: 'awesome-project',
link: 'https://github.com/adempiere/adempiere-vue/issues/2312'
},
{
text: 'vue-countTo',
link: 'https://github.com/adempiere/vue-countTo'
},
{
text: 'vue-split-pane',
link: 'https://github.com/adempiere/vue-split-pane'
},
{
text: 'awesome-bookmarks',
link: 'https://github.com/adempiere/awesome-bookmarks',
type: 'ZH'
}
]
},
{
textEN: 'Help',
textES: 'Ayuda',
textZH: '帮助',
items: [
{
textZH: '国内文档(解决Github.io访问慢的问题)',
link: 'https://adempiere-vue.gitee.io/adempiere-vue-site/zh'
},
{
text: 'Gitter',
textZH: 'Gitter讨论组',
link: 'https://gitter.im/adempiere-vue/discuss'
},
{
textZH: '作者Blog',
link: 'https://jianshiapp.com/circles/1209',
type: 'ZH'
},
{
textZH: '常见问题',
link: '/guide/other/faq.md',
type: 'ZH'
},
{
textZH: 'QQ群',
link: 'https://github.com/adempiere/adempiere-vue/issues/602',
type: 'ZH'
},
{
textZH: '作者个人微博',
link: 'https://weibo.com/u/3423485724',
type: 'ZH'
},
{
text: 'Changelog',
textES: 'Registro de cambios',
textZH: '更新记录',
link: 'https://github.com/adempiere/adempiere-vue/releases'
}
]
}
]
var BackendNav = [
{
text: 'Backend',
textES: 'Backend',
textZH: '后端整合',
items: [
{
text: 'Java backend integration',
textES: 'Java backend integration',
textZH: 'Java 后端整合',
link:
'https://store.akveo.com/products/vue-java-admin-dashboard-spring?utm_campaign=akveo_store-Vue-Vue_demo%2Fgithub&utm_source=vue_admin&utm_medium=referral&utm_content=vue_featires'
}
]
}
]
var ComponentNav = [
{
text: 'Component',
textES: 'Componente',
textZH: '组件',
items: [
{
text: 'Rich Text Editor',
textES: 'Editor de Texto Enriquecido',
textZH: '富文本',
link: '/feature/component/rich-editor.md'
},
{
text: 'Markdown Editor',
textES: 'Editor de Markdown',
textZH: 'Markdown 编辑器',
link: '/feature/component/markdown-editor.md'
},
{
text: 'Svg Icon',
textES: 'Icono Svg',
textZH: 'Svg Icon 图标',
link: '/feature/component/svg-icon.md'
},
{
text: 'Clipboard',
textES: 'Portapapeles',
textZH: '复制粘贴',
link: '/feature/component/clipboard.md'
},
{
text: 'Excel',
textZH: 'Excel',
link: '/feature/component/excel.md'
},
{
text: 'Pagination',
textES: 'Paginación',
textZH: 'Pagination 分页',
link: '/feature/component/pagination.md'
},
{
text: 'Tree Table',
textES: 'Tabla de Árbol',
textZH: 'Tree Table 树形表格',
link: '/feature/component/tree-table.md'
}
]
},
{
text: 'Script',
items: [
{
text: 'Svgo',
link: '/feature/script/svgo.md'
},
{
text: 'New',
textES: 'Nuevo',
link: '/feature/script/new.md'
}
]
}
]
module.exports = {
EcosystemNav,
ComponentNav,
BackendNav
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

View File

@ -0,0 +1,20 @@
{
"name": "adempiere-vue-doc",
"short_name": "adempiere-vue-doc",
"icons": [
{
"src": "/icons/192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icons/512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"start_url": "/index.html",
"display": "standalone",
"background_color": "#fff",
"theme_color": "#3eaf7c"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

27
docs/.vuepress/style.styl Normal file
View File

@ -0,0 +1,27 @@
.getting-started .outbound,
.nav-links .outbound {
display: none !important;
}
.getting-started a img {
margin-right: 8px !important;
}
.getting-started a img.no-margin {
margin-right: 0px !important;
}
//global
p code {
// color: #e96900!important;
// background-color: #f8f8f8!important;
// margin: 0 2px!important;
// padding: 0.25rem 0.4rem!important;
}
blockquote {
font-size: 15px !important;
color: #858585 !important;
border-left: 0.25rem solid #42b983 !important;
font-weight: 1000 !important;
}

View File

@ -0,0 +1,189 @@
<template>
<div class="home">
<div class="hero">
<img v-if="data.heroImage" :src="$withBase(data.heroImage)" alt="hero">
<h1>{{ data.heroText || $title || 'Hello' }}</h1>
<p class="description">{{ data.tagline || $description || 'Welcome to your VuePress site' }}</p>
<p class="action" v-if="data.actionText && data.actionLink">
<NavLink class="action-button" :item="actionLink"/>
</p>
</div>
<div class="features" :class="{isCN:isCN}" v-if="data.features && data.features.length">
<div class="feature" v-for="(feature, index) in data.features" :key="index">
<h2>{{ feature.title }}</h2>
<p>{{ feature.details }}</p>
</div>
</div>
<Content custom/>
<div class="footer" v-if="data.footer">{{ data.footer }}</div>
</div>
</template>
<script>
import NavLink from '@default-theme/NavLink.vue'
export default {
components: { NavLink },
computed: {
isCN() {
return this.$lang === 'zh-CN'
},
data() {
return this.$page.frontmatter
},
actionLink() {
return {
link: this.data.actionLink,
text: this.data.actionText
}
}
},
methods: {
clickCoding(tag) {
ga('send', 'click', 'e.coding', 'Action', tag)
}
}
}
</script>
<style lang="stylus">
@import '~@default-theme/styles/config.styl';
.home {
padding: $navbarHeight 2rem 0;
max-width: 960px;
margin: 0px auto;
.hero {
text-align: center;
>img {
max-height: 280px;
display: block;
margin: 3rem auto 1.5rem;
}
h1 {
font-size: 3rem;
}
h1, .description, .action {
margin: 1.8rem auto;
}
.description {
max-width: 35rem;
font-size: 1.6rem;
line-height: 1.3;
color: lighten($textColor, 40%);
}
.action-button {
display: inline-block;
font-size: 1.2rem;
color: #fff;
background-color: $accentColor;
padding: 0.8rem 1.6rem;
border-radius: 4px;
transition: background-color 0.1s ease;
box-sizing: border-box;
border-bottom: 1px solid darken($accentColor, 10%);
&:hover {
background-color: lighten($accentColor, 10%);
}
}
}
.features {
border-top: 1px solid $borderColor;
padding: 1.2rem 0;
margin-top: 2.5rem;
display: flex;
flex-wrap: wrap;
align-items: flex-start;
align-content: stretch;
justify-content: space-between;
}
.feature {
flex-grow: 1;
flex-basis: 30%;
max-width: 30%;
h2 {
font-size: 1.4rem;
font-weight: 500;
border-bottom: none;
padding-bottom: 0;
color: lighten($textColor, 10%);
}
p {
color: lighten($textColor, 25%);
}
}
.footer {
padding: 2.5rem;
border-top: 1px solid $borderColor;
text-align: center;
color: lighten($textColor, 25%);
}
}
@media (max-width: $MQMobile) {
.home {
.features {
flex-direction: column;
}
.feature {
max-width: 100%;
padding: 0 2.5rem;
}
}
}
@media (max-width: $MQMobileNarrow) {
.home {
padding-left: 1.5rem;
padding-right: 1.5rem;
.hero {
img {
max-height: 210px;
margin: 2rem auto 1.2rem;
}
h1 {
font-size: 2rem;
}
h1, .description, .action {
margin: 1.2rem auto;
}
.description {
font-size: 1.2rem;
}
.action-button {
font-size: 1rem;
padding: 0.6rem 1.2rem;
}
}
.feature {
h2 {
font-size: 1.25rem;
}
}
}
}
</style>

View File

@ -0,0 +1,345 @@
<template>
<div
class="theme-container"
:class="pageClasses"
@touchstart="onTouchStart"
@touchend="onTouchEnd"
>
<!-- <div v-if="isHome||isDonate" id="codefund" class="home-codefund" /> -->
<Navbar v-if="shouldShowNavbar" @toggle-sidebar="toggleSidebar" />
<div class="sidebar-mask" @click="toggleSidebar(false)" />
<Sidebar :items="sidebarItems" @toggle-sidebar="toggleSidebar">
<div slot="top" :class="{'load-success':loadSuccess}">
<div v-if="!isHome" id="codefund" :key="$route.path" />
</div>
<slot slot="bottom" name="sidebar-bottom" />
</Sidebar>
<div v-if="$page.frontmatter.layout" class="custom-layout">
<component :is="$page.frontmatter.layout" />
</div>
<Home v-else-if="$page.frontmatter.home" />
<Page v-else :sidebar-items="sidebarItems">
<slot slot="top" name="page-top" />
<slot slot="bottom" name="page-bottom" />
</Page>
<SWUpdatePopup :update-event="swUpdateEvent" />
</div>
</template>
<script>
import Vue from 'vue'
import nprogress from 'nprogress'
import Navbar from '@default-theme/Navbar.vue'
import Page from '@default-theme/Page.vue'
import Sidebar from '@default-theme/Sidebar.vue'
import SWUpdatePopup from '@default-theme/SWUpdatePopup.vue'
import { resolveSidebarItems } from '@default-theme/util'
import Swal from 'sweetalert2'
import Home from './Home.vue'
import { loadGitter/*, loadCarbon*/ } from './utils'
export default {
components: {
Home,
Page,
Sidebar,
Navbar,
SWUpdatePopup
},
data() {
return {
isSidebarOpen: false,
swUpdateEvent: null,
loadSuccess: true
}
},
computed: {
isHome() {
const page = this.$page
const { path } = page
if (path === '/zh/' || path === '/') {
return true
}
return false
},
isDonate() {
const page = this.$page
const { path } = page
return path.includes('donate')
},
isCN() {
return this.$lang === 'zh-CN'
},
shouldShowNavbar() {
const { themeConfig } = this.$site
const { frontmatter } = this.$page
if (frontmatter.navbar === false || themeConfig.navbar === false) {
return false
}
return (
this.$title ||
themeConfig.logo ||
themeConfig.repo ||
themeConfig.nav ||
this.$themeLocaleConfig.nav
)
},
shouldShowSidebar() {
const { frontmatter } = this.$page
return (
!frontmatter.layout &&
!frontmatter.home &&
frontmatter.sidebar !== false &&
this.sidebarItems.length
)
},
sidebarItems() {
return resolveSidebarItems(
this.$page,
this.$route,
this.$site,
this.$localePath
)
},
pageClasses() {
const userPageClass = this.$page.frontmatter.pageClass
return [
{
'no-navbar': !this.shouldShowNavbar,
'sidebar-open': this.isSidebarOpen,
'no-sidebar': !this.shouldShowSidebar
},
userPageClass
]
}
},
watch: {
$route: {
handler(val, oldVal) {
if (this.$isServer) return
if (document.getElementById('carbonads')) {
window._carbonads && window._carbonads.refresh()
}
},
immediate: true
}
},
mounted() {
loadGitter()
// loadCarbon()
window.addEventListener('scroll', this.onScroll)
// configure progress bar
nprogress.configure({ showSpinner: false })
this.$router.beforeEach((to, from, next) => {
if (to.path !== from.path && !Vue.component(to.name)) {
nprogress.start()
}
next()
})
this.$router.afterEach(() => {
nprogress.done()
this.isSidebarOpen = false
})
this.$on('sw-updated', this.onSWUpdated)
// this.checkAdBlock()
},
methods: {
checkLang() {
let lang = navigator.language || navigator.userLanguage // IE
lang = lang.substr(0, 2) // lang2
if (lang === 'zh') {
return 'cn'
}
return 'en'
},
adBlockDetected() {
const cn =
'检测到你使用了例如AdBlock之类的广告屏蔽插件请将本项目加入白名单中。因为广告收入对于一个开源项目来说真的很重要。拜托了🙏'
const en =
'It is detected that you have used an ad blocking plug-in such as AdBlock, etc. to replace this item and add it to the whitelist. Because advertising revenue is really important for an open source project. Please, 🙏'
Swal.fire({
title: this.checkLang() === 'cn' ? cn : en,
width: 600,
padding: '3em',
allowOutsideClick: false,
allowEscapeKey: false,
showConfirmButton: false,
backdrop: `
rgba(0,0,123,0.4)
url("${this.$withBase('/nyan-cat.gif')}")
left top
no-repeat`
})
this.sendGa(true)
},
adBlockNotDetected() {
this.sendGa(false)
},
checkAdBlock() {
import('blockadblock').then(() => {
const { blockAdBlock } = window
if (typeof blockAdBlock === 'undefined') {
this.adBlockDetected()
} else {
blockAdBlock.onDetected(this.adBlockDetected)
blockAdBlock.onNotDetected(this.adBlockNotDetected)
}
})
},
sendGa(tag) {
window.ga &&
window.ga('send', 'event', {
eventCategory: 'adblock',
eventAction: tag
})
},
loadError(oError) {
this.loadSuccess = false
},
toggleSidebar(to) {
this.isSidebarOpen = typeof to === 'boolean' ? to : !this.isSidebarOpen
},
// side swipe
onTouchStart(e) {
this.touchStart = {
x: e.changedTouches[0].clientX,
y: e.changedTouches[0].clientY
}
},
onTouchEnd(e) {
const dx = e.changedTouches[0].clientX - this.touchStart.x
const dy = e.changedTouches[0].clientY - this.touchStart.y
if (Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > 40) {
if (dx > 0 && this.touchStart.x <= 80) {
this.toggleSidebar(true)
} else {
this.toggleSidebar(false)
}
}
},
onSWUpdated(e) {
this.swUpdateEvent = e
}
}
}
</script>
<style src="prismjs/themes/prism-tomorrow.css">
</style>
<style src="@default-theme/styles/theme.styl" lang="stylus"></style>
<style>
.cf-wrapper {
z-index: 101 !important;
}
.cf-wrapper a {
display: block;
}
.sidebar .sidebar-links {
padding-top: 10px;
}
.load-success {
position: sticky;
top: 0;
background: #fff;
}
.swal2-title {
font-size: 20px !important;
line-height: 30px !important;
text-align: left !important;
}
</style>
<style>
#carbonads {
display: flex;
max-width: 280px;
background-color: hsl(0, 0%, 98%);
box-shadow: 0 1px 4px 1px hsla(0, 0%, 0%, 0.1);
z-index: 2;
position: fixed;
right: 0;
bottom: 60px;
}
#carbonads a {
color: inherit;
text-decoration: none;
}
#carbonads a:hover {
color: inherit;
}
#carbonads span {
position: relative;
display: block;
overflow: hidden;
}
#carbonads .carbon-wrap {
display: flex;
}
.carbon-img {
display: block;
margin: 0;
line-height: 1;
}
.carbon-img img {
display: block;
}
.carbon-text {
font-size: 13px;
padding: 10px;
line-height: 1.4;
text-align: left;
}
.carbon-poweredby {
display: block;
padding: 8px 10px;
background: repeating-linear-gradient(
-45deg,
transparent,
transparent 5px,
hsla(0, 0%, 0%, 0.025) 5px,
hsla(0, 0%, 0%, 0.025) 10px
)
hsla(203, 11%, 95%, 0.4);
text-align: center;
text-transform: uppercase;
letter-spacing: 0.5px;
font-weight: 600;
font-size: 9px;
line-height: 1;
}
</style>

View File

@ -0,0 +1,40 @@
const dynamicLoadScript = (src, callback, id) => {
const existingScript = document.getElementById(src)
const cb = callback || function() {}
if (!existingScript) {
const script = document.createElement('script')
script.src = src // src url for the third-party library being loaded.
script.id = id || src
script.async = true
document.body.appendChild(script)
const onEnd = 'onload' in script ? stdOnEnd : ieOnEnd
onEnd(script, cb)
}
if (existingScript && cb) cb(null, existingScript)
function stdOnEnd(script, cb) {
script.onload = function() {
// this.onload = null here is necessary
// because even IE9 works not like others
this.onerror = this.onload = null
cb(null, script)
}
script.onerror = function() {
this.onerror = this.onload = null
cb(new Error('Failed to load ' + src), script)
}
}
function ieOnEnd(script, cb) {
script.onreadystatechange = function() {
if (this.readyState !== 'complete' && this.readyState !== 'loaded') return
this.onreadystatechange = null
cb(null, script) // there is no way to catch loading errors in IE8
}
}
}
export default dynamicLoadScript

View File

@ -0,0 +1,47 @@
import axios from 'axios'
import dynamicLoadScript from './dynamic-load-script'
export function getCodefund(template = 'default') {
const codefundId = isGitee() ? '79' : '116'
axios
.get(
`https://codefund.io/properties/${codefundId}/funder.html?template=${template}`
)
.then(function(response) {
document.getElementById('codefund').innerHTML = response.data
})
}
export function isGitee() {
const origin = window.location.origin
if (origin.includes('gitee.io')) {
return true
}
return false
}
export function loadCarbon() {
const id = '_carbonads_js'
const existingScript = document.getElementById(id)
if (existingScript) return
const script = document.createElement('script')
document.body.appendChild(script)
dynamicLoadScript(
'https://cdn.carbonads.com/carbon.js?serve=CE7IK5QY&placement=panjiachengithubio',
() => {},
id
)
}
export function loadGitter() {
const id = 'adempiere-vue/discuss'
const existingScript = document.getElementById(id)
if (existingScript) return
const script = document.createElement('script')
script.id = id
script.text =
"((window.gitter = {}).chat = {}).options = {room: 'adempiere-vue/discuss'};"
document.body.appendChild(script)
dynamicLoadScript('https://sidecar.gitter.im/dist/sidecar.v1.js')
}

47
docs/.vuepress/utils.js Normal file
View File

@ -0,0 +1,47 @@
function deepClone(d) {
return JSON.parse(JSON.stringify(d))
}
function genNav(items, type = 'EN') {
return items.filter(v => {
if (v.type && v.type !== type) return false
if (v[`text${type}`]) {
v.text = v[`text${type}`]
}
if (type != 'EN' && v.link && !isExternalLink(v.link)) {
v.link = `/${type.toLocaleLowerCase()}${v.link}`
}
if (v.items && v.items.length > 0) {
v.items = genNav(v.items, type)
}
return v
})
}
function validateURL(textval) {
const urlregex = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%*$#=~_-]+))*$/
return urlregex.test(textval)
}
function isExternalLink(routePath) {
return validateURL(routePath)
}
function getComponentSidebar(item, type = 'EN') {
return item[0].items.map(v => {
if (type != 'EN' && v.link && !isExternalLink(v.link)) {
v.link = `/${type.toLocaleLowerCase()}${v.link}`
}
return v.link
})
}
module.exports = {
genNav,
getComponentSidebar,
deepClone
}

38
docs/README.md Executable file
View File

@ -0,0 +1,38 @@
---
home: true
heroImage: /home.png
title: a
actionText: Get Started →
actionLink: /guide/
features:
- title: Feature-rich
details: A strong framework of ADempiere for many enterprise applications and various components
- title: Best Practice
details: Reasonable framework choice, good engineering practice
- title: Up-to-date Dev Stack
details: Development using front-end advanced technology
- title: Permission Validation
details: Dynamic loading of route and rendering sidebar based on permissions
- title: Globalization
details: Built-in industry universal international solution
- title: Theming
details: Supports multiple dynamic skin methods
footer: GNU/GPL v3 Licensed | Copyright © 2019-present ADempiere
---
## Getting Started
```bash
# clone the project
git clone https://github.com/adempiere/adempiere-vue.git
# install dependency
yarn install
# develop
yarn dev
```
## Demo
[ADempiere UI Demo](https://demo-ui.erpya.com/)

9
docs/donate/README.md Normal file
View File

@ -0,0 +1,9 @@
---
sidebar: false
---
::: tip Donate
If you find this project useful, you can help to maintain this project :tropical_drink:
:::
[PayPal](https://www.paypal.me/YamelSenih)

38
docs/es/README.md Normal file
View File

@ -0,0 +1,38 @@
---
home: true
heroImage: /home.png
title: a
actionText: Empezar →
actionLink: /es/guide/
features:
- title: Rico en características
details: Plantillas típicas para aplicaciones empresariales y varios componentes
- title: Mejores Prácticas
details: Elección razonable de framework, buenas prácticas de ingeniería
- title: Actualización de Dev Stack
details: Desarrollo utilizando tecnología avanzada front-end
- title: Validación de Permisos
details: Carga dinámica de ruta y barra lateral basada en permisos
- title: Globalización
details: Solución internacional universal integrada en la industria
- title: Tematización
details: Soporta múltiples métodos de temas dinámicos.
footer: Licencia MIT | Derechos de autor © 2017-presente PanJiaChen
---
## Empezando
```bash
# clonar el proyecto
git clone https://github.com/adempiere/adempiere-vue.git
# instalar dependencias
yarn
# Correr cliente
yarn dev
```
## Demo
[ADempiere UI Demo](https://demo-ui.erpya.com/)

9
docs/es/donate/README.md Normal file
View File

@ -0,0 +1,9 @@
---
sidebar: false
---
::: tip Donar
Si encuentras útil este proyecto, puedes ayudar a manter el proyecto :tropical_drink:
:::
[PayPal](https://www.paypal.me/YamelSenih)

View File

@ -0,0 +1,65 @@
---
sidebarDepth: 3
---
# Portapapeles
Aquí está el copiar y pegar basado en [portapapeles](https://github.com/zenorocha/clipboard.js)
Este proyecto ofrece dos maneras de usarlo.
## Usar directamente
```html
<el-button @click='handleCopy(inputData,$event)'>copiar</el-button>
```
```js
import clip from '@/utils/clipboard.js' // usar el portapapeles directamente
methods: {
handleCopy(text, event) {
clip(text, event)
}
}
```
En primer lugar, importa `clipboard.js` y configura la función `click`.
`clip()` El primer parámetro es el contenido a copiar, el segundo parámetro es el evento event. Ambos parámetros son necesarios.
<br/>
<br/>
## v-directive
Este proyecto también encapsula un `v-clipboard`.
```html
<el-button
v-clipboard:copy='inputData'
v-clipboard:success='clipboardSuccess'>
copiar
</el-button>
```
```js
import clipboard from '@/directive/clipboard/index.js' // usar el portapapeles por v-directive
directives: {
clipboard
},
methods: {
clipboardSuccess() {
this.$message({
message: 'Copiado exitosamente',
type: 'success',
duration: 1500
})
}
}
```
`v-clipboard:copy`: La copia del contenido.
`v-clipboard:success`: Función de devolución de llamada de éxito (callback).

View File

@ -0,0 +1,104 @@
# Excel
## Exportar Excel
La importación y exportación de Excel se implementa confiando en [js-xlsx](https://github.com/SheetJS/js-xlsx).
[Export2Excel.js](https://github.com/adempiere/adempiere-vue/blob/master/src/vendor/Export2Excel.js) está empaquetado en el `js-xlsx` para facilitar la exportación de datos.
### Uso
Dado que `Export2Excel` depende no solo de `js-xlsx` sino también de `file-saver` y `script-loader`.
Primero debes instalar el siguiente comando:
```bash
npm install xlsx file-saver -S
npm install script-loader -S -D
```
Dado que el tamaño `js-xlsx` todavía es muy grande, la función de exportación no es una función muy común, por lo que se recomienda una carga lenta al usarla. El método de uso es el siguiente:
```js
import('@/vendor/Export2Excel').then(excel => {
excel.export_json_to_excel({
header: tHeader, //Se requiere encabezado
data, //Datos específicos requeridos
filename: 'excel-list', //Opcional
autoWidth: true, //Opcional
bookType: 'xlsx' //Opcional
})
})
```
:::warning Advertencia <Badge text="v3.9.1+"/>
El código de compatibilidad para Blob se ha eliminado en las versiones posteriores a `v3.9.1 +`. Si necesita ser compatible con navegadores de muy bajo nivel, puedes introducir manualmente [blob-polyfill](https://www.npmjs.com/package/blob-polyfill).
:::
### Parámetros
| Parámetros | Descripción | Tipo | Valores Aceptados | Predeterminado |
| ---------- | -------------------------------- | ------- | ----------------------------------------------------------------------------------- | -------------- |
| header | Exportar encabezado de datos | Array | / | [] |
| data | Datos específicos exportados | Array | / | [] |
| filename | Nombre de archivo de exportación | String | / | excel-list |
| autoWidth | Si la celda de ancho automático | Boolean | true / false | true |
| bookType | Tipo de archivo de exportación | String | xlsx, csv, txt, [more](https://github.com/SheetJS/js-xlsx#supported-output-formats) | xlsx |
### Ejemplo
```js
import('@/vendor/Export2Excel').then(excel => {
const tHeader = ['Id', 'Title', 'Author', 'Readings', 'Date']
const data = this.list
excel.export_json_to_excel({
header: tHeader, //Se requiere encabezado
data, //Datos específicos requeridos
filename: 'excel-list', //Opcional
autoWidth: true, //Opcional
bookType: 'xlsx' //Opcional
})
})
```
- [Demo en línea](https://adempiere.github.io/adempiere-vue/#/excel/export-excel)
- [Código en línea](https://github.com/adempiere/adempiere-vue/blob/master/src/views/excel/export-excel.vue)
## Importación de Excel
El componente encapsulado de importación de Excel [UploadExcel](https://github.com/adempiere/adempiere-vue/blob/master/src/components/UploadExcel/index.vue), soporta clic, arrastrar y cargar, también depende de `js-xlsx`.
Proporciona dos funciones de devolución de llamada (callback):
- beforeUpload
Puedes hacer algunos juicios especiales antes de subir. Por ejemplo, si el tamaño del archivo es mayor que 1 megabyte? Si es superior a 1 megabyte, deja de analizarlo y muestra un mensaje de error.
```js
beforeUpload(file) {
const isLt1M = file.size / 1024 / 1024 < 1
if (isLt1M) {
return true
}
this.$message({
message: 'Por favor, no cargue archivos de más de 1m de tamaño.',
type: 'warning'
})
return false
}
```
- onSuccess
Una función de devolución de llamada (callback) que se activa cuando el análisis se realiza correctamente, que devuelve el encabezado y el contenido de la tabla.
```js
handleSuccess({ results, header }) {
this.tableData = results
this.tableHeader = header
}
```
- [Demo en línea](https://adempiere.github.io/adempiere-vue/#/excel/upload-excel)
- [Código en línea](https://github.com/adempiere/adempiere-vue/blob/master/src/views/excel/upload-excel.vue)

View File

@ -0,0 +1,76 @@
# Editor Markdown <Badge text="v3.9.3+"/>
Originalmente utilizado [simplemde-markdown-editor](https://github.com/sparksuite/simplemde-markdown-editor) como editor de markdown, pero esta biblioteca no se ha actualizado ni mantenido desde hace mucho tiempo, y también existe el riesgo de xss. Así que después de la versión <Badge text="v3.9.3+"/>, usa [tui.editor](https://github.com/nhnent/tui.editor) como el nuevo editor. Todos los documentos siguientes están basados en tui.editor. [Más contenido](https://github.com/nhnent/tui.editor).
## Propiedades
| Nombre | Tipo | Predeterminado | Descripción |
| -------- | ------ | -------------------------- | ------------------------------------------------------------------------------------------------ |
| value | String | " " | Esta propiedad puede cambiar el contenido del editor. **Si estás usando `v-model`, no lo uses**. |
| options | Object | following `defaultOptions` | Opciones de tui.editor. Esto es para inicializar tui.editor. |
| height | String | '300px' | Esta propiedad puede controlar la altura del editor. |
| mode | String | 'markdown' | Esta propiedad puede cambiar el modo del editor. (`markdown`or `wysiwyg`) |
| language | String | 'en_US' | i18n |
```js
const defaultOptions = {
minHeight: '200px',
previewStyle: 'vertical',
useCommandShortcut: true,
useDefaultHTMLSanitizer: true,
usageStatistics: false,
hideModeSwitch: false,
toolbarItems: [
'heading',
'bold',
'italic',
'strike',
'divider',
'hr',
'quote',
'divider',
'ul',
'ol',
'task',
'indent',
'outdent',
'divider',
'table',
'image',
'link',
'divider',
'code',
'codeblock'
]
}
```
## Métodos
- setValue
- getValue
- setHtml
- getHtml
## Ejemplo
```html
<template>
<markdown-editor v-model="content" />
</template>
<script>
import MarkdownEditor from '@/components/MarkdownEditor'
export default {
data() {
return {
content: '',
}
}
}
</script>
```
## Ejemplo en Linea
[enlace](https://adempiere.github.io/adempiere-vue/#/components/markdown)

View File

@ -0,0 +1,62 @@
# Paginación <Badge text="v3.9.2+"/>
El componente de paginación se basa principalmente en el elemento 'el-pagination' para el empaquetado secundario, y expandió la función de desplazamiento automático (auto-scroll).
## Uso básico
```html
<template>
<pagination
:total="total"
:page.sync="listQuery.page"
:limit.sync="listQuery.limit"
@pagination="getList" />
</template>
<script>
import Pagination from '@/components/Pagination'
export default {
components: { Pagination },
data() {
return {
total: 0,
listQuery: {
page: 1,
limit: 20
}
}
},
methods: {
getList() {
// Obtener datos
}
}
}
</script>
```
## Atributos
| Atributo | Descripción | Tipo | Predeterminado |
| :---------: | :-------------------------------------------------------------------------- | :-------: | :-------------: |
| total | recuento total de artículos | Number | / |
| page | número de página actual, soporta el modificador .sync | Number | 1 |
| limit | Recuento de elementos de cada página, admite el modificador .sync | Number | 20 |
| page-sizes | Opciones de conteo de artículos por página | Number [] | 10, 20, 30, 50] |
| hidden | si ocultar | Boolean | false |
| auto-scroll | Si se desplaza automáticamente a la parte superior después de la paginación | Boolean | true |
También se admite soporte de otros atributos del elemento `el-pagination`. Consulte la [Documentación](http://element.eleme.io/#/zh-CN/component/pagination) para obtener más detalles.
## Eventos
| Nombre del evento | Descripción | Parámetros |
| ----------------- | ---------------------------------------------- | ---------------- |
| pagination | Se dispara cuando cambia el límite o la página | {pagina, límite} |
## Código fuente y Demo
- [Código fuente](https://github.com/adempiere/adempiere-vue/blob/master/src/components/Pagination/index.vue)
- [Demo en línea](https://adempiere.github.io/adempiere-vue/#/table/complex-table)

View File

@ -0,0 +1,49 @@
---
sidebarDepth: 3
---
# Editor de texto enriquecido
El editor de texto enriquecido es una parte fundamental del sistema de administración, pero al mismo tiempo es un lugar con muchos problemas. En el proceso de selección de textos ricos, también caminé muchos desvíos. Los editores de texto enriquecido comunes en el mercado se utilizan básicamente, y finalmente se eligió [Tinymce](https://github.com/tinymce/tinymce).
Aquí hay una breve introducción a las razones por las que se recomienda `tinymce`: `tinymce` es un veterano para hacer una compañía de texto enriquecido (aquí también se recomienda `ckeditor`, también es una compañía que ha estado haciendo texto enriquecido, la nueva versión es muy bueno), sus productos han superado la prueba del mercado y cuentan con documentación detallada y una rica configuración. Una de las claves para usar texto enriquecido es copiar el formato. Antes de usar un texto coreano rico `summernote`, me desperdició mucho tiempo, muy hostil. Pero el formato de `tinymce` es bastante bueno. También tiene una característica de valor agregado: es una pasta de poder, es extremadamente potente, soporta copiar todo desde Word o cualquier otro lugar. La extensibilidad también es crítica para el texto enriquecido. Uso `tinymce` para escribir varios complementos, los costos de aprendizaje y la facilidad de estudio son buenos y muy fáciles de expandir. El último punto es que la documentación es muy buena, básicamente desea obtener el elemento de configuración, tiene. Tinymce también admite la carga bajo demanda, puede personalizar los complementos a través de su página de compilación oficial.
Déjame analizar algunos de los otros textos ricos en el mercado:
- **[summernote](https://github.com/summernote/summernote)** Permítame comenzar con un texto enriquecido que definitivamente no recomendaría. Es inconsistente con muchos comportamientos predeterminados reconocidos entre otros. Y solo para el uso de una función de diálogo, importan el bootstrap, un grupo de personas protestan. El formateo también es muy malo. ¡No lo uses de todos modos! ¡No lo uses! ¡No lo uses!
- **[ckeditor](https://github.com/galetahub/ckeditor)** Ckeditor también es una empresa veterana que hace texto enriquecido, solía usarlo en el proyecto de la empresa. Este año, la versión 5.0 de la interfaz de usuario también se ha vuelto más bonita, bastante buena y tiene los complementos más ricos. Se recomienda que lo intentes.
- **[quill](https://github.com/quilljs/quill)** También es un texto rico muy caliente, la piel es muy elegante. Escribir un plug-in basado en él también es muy simple. El diseño de la API es muy bueno. La razón por la que no lo elegí fue porque no era buena para la imagen de operación y era difícil de cambiar. Si no hay operación de la imagen del usuario, se recomienda.
- **[medium-_editor_](https://github.com/yabwe/medium-editor)** El famoso texto rico en medio (producido de forma no oficial), pero el grado de finalización no es muy bueno, la escalabilidad no es mala . Sin embargo, creo que la mayoría de los usuarios todavía no se utilizarán en esta forma de escritura.
- **[Squire](https://github.com/neilj/Squire)** Un texto enriquecido relativamente ligero, comprimido solo 11.5kb, en relación con otro texto enriquecido es muy pequeño, las características recomendadas no son una sugerencia complicada.
- **[UEditor](http://ueditor.baidu.com/website/index.html)** No se usa en profundidad, solo se usa un proyecto simple en angular1X, pero ui realmente feo, no cumple con la estética actual , el funcionario también ha pasado mucho tiempo sin ir con el nuevo.
- **[slate](https://github.com/ianstormtaylor/slate)** Un marco completamente personalizable para crear editores de texto enriquecido. Slate te permite crear editores ricos e intuitivos como los de Medium, Dropbox Paper o Google Docs, que se están convirtiendo en una mesa de apuestas para aplicaciones en la web, sin que tu base de código se vuelva más compleja. Se ve bien, después de una oportunidad practicaré en el proyecto, pruébalo.
Incluí una gran cantidad de texto enriquecido, pero no enumeré ningún texto enriquecido relacionado con vue, principalmente porque el texto enriquecido es realmente más complejo de lo que se pensaba. También dicho en el artículo anterior, de hecho, los componentes de encapsulación vue son muy convenientes, no hay necesidad de usar el paquete de cosas de otra persona.
Qué tipo de vue-quill vue-editor es solo un paquete simple, sin dificultad. También puedes encapsularlo tú mismo, y ser un poco más flexible y controlable. Además, vue realmente no tiene ningún texto enriquecido bueno, a diferencia de reaccionar tiene [draft](https://github.com/facebook/draft-js) producido por facebook, [editor](https://github.com/ory/editor) producido por ory. Vue no tiene este producto de una gran empresa.
Por supuesto, también puede elegir algún editor de texto enriquecido pagado, la propia compañía del autor tiene un proyecto en el uso del [froala-editor](https://www.froala.com/wysiwyg-editor). Si es hermoso y fácil de usar, la compañía compró una versión profesional, $ 349 al año, el precio también es muy razonable, pero de hecho, ahorrar el costo del desarrollo del desarrollador puede ir mucho más allá del precio.
## Tinymce
Aquí para hablar brevemente sobre el uso de Tinymce en sus propios proyectos.
After <Badge text="v4.2.0+"/> will dynamic import tinymce by `CDN` .
If you want to change the cdn address or the version of tinymce, just find tinymce cdn in [@/components/Tinymce](https://github.com/adempiere/adempiere-vue/blob/master/src/components/Tinymce/index.vue) then modified it. It will be automatically injected into `index.html` via `dynamicLoadScript`.
> El uso actual del método 'Tinymce' de la instalación de npm es más complejo y tiene algunos problemas (que pueden usarse en el futuro). :space_invader:
**Uso**
Debido a que el texto enriquecido no es adecuado para datos de dos vías, solo vea los cambios de contenido una vez, y luego no volverá a verse. Si más tarde necesita cambiar el contenido de texto enriquecido. Puede configurarse por `this.refs.xxx.setContent()`.
El código fuente también es muy simple, cualquier otra necesidad se puede modificar en `@/components/Tinymce/index.vue`.
```html
<tinymce :height="300" v-model="content" id='tinymce'></tinymce>
```

View File

@ -0,0 +1,38 @@
---
sidebarDepth: 3
---
# Icono Svg
Componente global de icono: Icono Svg.
Por defecto, el componente Icono Svg está registrado en [@/icons](https://github.com/adempiere/adempiere-vue/blob/master/src/icons/index.js#L6), y puede usarse en cualquier parte del proyecto. Todos los iconos se pueden encontrar en [@/icons/svg](https://github.com/adempiere/adempiere-vue/tree/master/src/icons/svg). Puedes agregar o eliminar el icono por ti mismo, y el icono se importará automáticamente sin operación manual.
## Uso
```html
<!-- usa icon-class para configurar el nombre; usa `class-name` para personalizar la clase -->
<svg-icon icon-class="password" class-name='custom-class' />
```
## Cambiar color
Por defecto, `svg-icon` lee su color primario `fill: currentColor;`
Puedes cambiar el `color` del padre o directamente el color de relleno `fill`.
## Importar desde url <Badge text="v4.2.0+"/>
Soporte de importación `svg` desde url externa. P.ej:
`<svg-icon icon-class="https://xxxx.svg />`
## Tamaño
Si estás descargando un icono de [iconfont](https://www.iconfont.cn/), recuerda usar una herramienta como Sketch para especificar el tamaño del icono. De lo contrario, el tamaño de los iconos en el proyecto puede no ser uniforme.
Todos los iconos utilizados en este proyecto tienen especificaciones de tamaño 128\*128.
:::tip
Si encuentras el color incorrecto del icono, puedes consultar el [issue](https://github.com/adempiere/adempiere-vue/issues/330) para modificarlo
:::

View File

@ -0,0 +1,108 @@
# Tabla de Árbol
## Resumen
Este componente solo proporciona una solución para crear `TreeTable`. Se basa en el componente de tabla `element-ui`. Utiliza el método `row-style` de `el-table` para determinar si el elemento debe ocultarse o mostrarse.
Y este componente hace uso completo de las características (slot) de `vue` para que sea fácil de usar.
En `evel.js`, el método `addAttrs` agrega varias propiedades a los datos, y `treeTotable` aplana la matriz. Ninguna de estas operaciones destruirá los datos de origen, solo agregará propiedades.
## Propiedades
| Atributo | Descripción | Tipo | Predeterminado |
| :--------------: | :--------------------------------------------------------------- | :-----: | :------------: |
| data | datos de visualización originales | Array | [] |
| columns | atributo de columna | Array | [] |
| defaultExpandAll | si expandir todos los nodos por defecto | Boolean | false |
| defaultChildren | Especifique qué objeto de nodo se utiliza como subárbol del nodo | String | children |
| indent | Indentación horizontal de nodos en niveles adyacentes en píxeles | Number | 50 |
> Se admite cualquiera de las propiedades de `el-table`, como `border`, `fit`, `size` o `@select`, `@cell-click`. Consulta la documentación de ʻel-table` para más detalles.
---
### Ejemplo
```html
<tree-table :data="data" :columns="columns" border>
```
#### data(**Requerido**)
```js
const data = [
{
name:'1'
children: [
{
name: '1-1'
},
{
name: '1-2'
}
]
},
{
name: `2`
}
]
```
#### columns(**Requerido**)
- label: texto que se muestra en el encabezado
- key: data.key se mostrará en la columna
- expand: `true` o `false`
- checkbox: `true` o `false`
- width: ancho de columna, por ejemplo `200`
- align: alineación `left/center/right`
- header-align: alineación del encabezado de la tabla `left/center/right`
```javascript
const columns = [
{
label: 'Checkbox',
checkbox: true
},
{
label: '',
key: 'id',
expand: true
},
{
label: 'Event',
key: 'event',
width: 200,
align: 'left'
},
{
label: 'Scope',
key: 'scope'
}
]
```
> El componente de la tabla de árbol generará un slot con nombre basado en la propiedad key de las columnas. Si necesitas personalizar los datos de la columna, puedes hacerlo a través del slot.
```html
<template slot="your key" slot-scope="{scope}">
<el-tag>nivel: {{ scope.row._level }}</el-tag>
<el-tag>expandir: {{ scope.row._expand }}</el-tag>
<el-tag>seleccionar: {{ scope.row._select }}</el-tag>
</template>
```
## Eventos
Actualmente hay varios métodos disponibles, pero solo la versión `beta`, que probablemente se modifique más adelante.
```js
this.$refs.TreeTable.addChild(row, data) //Agregar elementos secundarios
this.$refs.TreeTable.addBrother(row, data) //Agregar un elemento hermano
this.$refs.TreeTable.delete(row) //Eliminar el elemento
```
## Otro
Si tienes otros requisitos, consulta la api [el-table](http://element-cn.eleme.io/#/en-US/component/table) para modificar el index.vue

View File

@ -0,0 +1,19 @@
# Nuevo <Badge text="v4.0.0+"/>
En el trabajo diario, lo más común es escribir módulos y componentes comerciales. Cada vez que necesitas una nueva `vista` o `componente` debes crear manualmente un nuevo `.vue`, crear un `<template>`, `<script>`, `<style>`, o algún problema.
Así que en la nueva versión, basada en [plop](https://github.com/amwmedia/plop), se proporcionan varias plantillas básicas para facilitar la creación de una nueva `vista` o `componente`.
Ejecuta el siguiente comando:
```bash
npm run new
```
![plop](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/5f8ea239-aaa5-4e91-9d09-ed56b33a110d.gif)
Como se muestra en el gif anterior, es fácil generar el fragmento de código básico que quiero simplemente presionando Enter varias veces. Esto es solo una demostración, puedes personalizar la plantilla según tus necesidades.
Para requisitos de plantilla adicionales, puedes crear una plantilla personalizada siguiendo la documentación de `plop` y dirigiéndote al directorio `plop-templates`.
De hecho, esta característica es similar a lo que hacen los `snippets`. Si crees que la configuración es demasiado complicada, puedes instalar un fragmento de código basado en VSCode como [Vue 2 Snippets](https://marketplace.visualstudio.com/items?itemName=hollowtree.vue-snippets).

View File

@ -0,0 +1,21 @@
# Svgo <Badge text="v3.9.0+"/>
Este proyecto proporciona optimización de procesamiento de compresión svg. Basada en [svgo](https://github.com/svg/svgo).
```bash
npm run svgo
```
Muchas descargas en línea o svg exportados por `Sketch` tendrán una gran cantidad de información redundante e inútil, aumentando considerablemente el tamaño del svg. Podemos optimizarlo con `svgo`. Por ejemplo, la siguiente figura es un svg exportado por `Sketch`
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/333edb6b-4b95-42f8-aa60-b8f42e516b52.jpg)
Podemos ejecutar `npm run svgo`
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/e7b1324e-cd67-4306-aebf-f659bcc433cf.jpg)
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/006c4bb5-b2d1-447d-a1c9-a912cf5dee47.jpg)
Se procesa información inútil.
Se puede utilizar una configuración más detallada en `/scr/icons/svgo.yml`.

233
docs/es/guide/README.md Normal file
View File

@ -0,0 +1,233 @@
---
pageClass: getting-started
---
# Introducción
**Nota: Esta documentación fue forkeada del proyecto original de [PanJiaChen](https://github.com/PanJiaChen/vue-element-admin-site). El crédito es para él puesto que fue el que inició este gran proyecto. Cualquier cambio después de forkeado será hecho por el [equipo de ADempiere](https://github.com/adempiere/adempiere)**
[![vue](https://img.shields.io/badge/vue-2.6.10-brightgreen.svg)](https://github.com/vuejs/vue)
[![element-ui](https://img.shields.io/badge/element--ui-2.7.0-brightgreen.svg)](https://github.com/ElemeFE/element)
[![Build Status](https://travis-ci.org/adempiere/adempiere-vue.svg?branch=master)](https://travis-ci.org/adempiere/adempiere-vue)
[![license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/adempiere/adempiere-vue/blob/master/LICENSE)
[![GitHub release](https://img.shields.io/github/release/PanJiaChen/adempiere-vue.svg)](https://github.com/adempiere/adempiere-vue/releases)
[![donate](https://img.shields.io/badge/%24-donate-ff69b4.svg)](https://adempiere-vue.gitee.io/adempiere-vue-site/zh/donate)
[![GitHub stars](https://img.shields.io/github/stars/adempiere/adempiere-vue.svg?style=social&label=Stars)](https://github.com/adempiere/adempiere-vue)
[adempiere-vue](http://adempiere.github.io/adempiere-vue) es una solución front-end lista para producción para interfaces de administración. Se basa en [vue](https://github.com/vuejs/vue) y utiliza el kit de interfaz de usuario [element-ui](https://github.com/ElemeFE/element).
Es un administrador vue mágico basado en el nuevo conjunto de desarrollo de vue, solución i18n incorporada, plantillas típicas para aplicaciones empresariales, muchas características increíbles. Te ayuda a construir grandes aplicaciones complejas de una sola página. Creo que sean cuales sean tus necesidades, este proyecto te ayudará.
:::tip
Este proyecto integra muchas características que no puedes usar, causará mucha redundancia de código. Si tu proyecto no presta atención a este problema, también puedes desarrollarlo directamente basándote en él.
De lo contrario, puedes usar [vue-admin-template](https://github.com/adempiere/vue-admin-template).
- Solución integrada: [adempiere-vue](https://github.com/adempiere/adempiere-vue)
- Plantilla Básica: [vue-admin-template](https://github.com/adempiere/vue-admin-template)
- Desktop: [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
- Typescript: [vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template) (Créditos: [@Armour](https://github.com/Armour))
- Otros: [awesome-project](https://github.com/adempiere/adempiere-vue/issues/2312)
:::
<br/>
## Características
```
- Iniciar sesión / Cerrar sesión
- Autenticación de Permisos
- Permiso de la página
- Permiso directivo
- Página de configuración de permisos
- Inicio de sesión en dos pasos
- Construcción multi-ambiente
- dev sit stage prod
- Características Globales
- I18n
- Múltiples temas dinámicos
- Barra lateral dinámica (admite enrutamiento multinivel)
- Breadcrumb dinámicos
- Etiquetas-vista (Página de pestaña Soporte de operación del clic derecho)
- Svg Sprite
- Datos simulados
- Pantalla completa
- Barra lateral responsiva
- Editor
- Editor de texto enriquecido
- Editor de Markdown
- Editor de JSON
- Excel
- Exportar Excel
- Subir Excel
- Visualización de Excel
- Exportar zip
- Tabla
- Tabla dinámica
- Arrastrar y soltar tabla
- Tabla de edición en línea
- Página de error
- 401
- 404
- Componentes
- Subir Avatar
- Volver Arriba
- Arrastrar Diálogo
- Arrastrar Seleccionar
- Drag Kanban
- Arrastrar Lista
- Panel Dividido
- Dropzone
- Sticky
- CountTo
- Ejemplo avanzado
- Registro de errores
- Tablero
- Guía de la página
- ECharts
- Portapapeles
- Markdown a html
```
<br/>
## Preparación
Necesitas instalar [node](http://nodejs.org/) y [git](https://git-scm.com/) locamente. El proyecto se basa en [ES2015+](http://es6.ruanyifeng.com/), [vue](https://cn.vuejs.org/index.html), [vuex](https://vuex.vuejs.org/zh-cn/), [vue-router](https://router.vuejs.org/zh-cn/), [vue-cli](https://github.com/vuejs/vue-cli), [axios](https://github.com/axios/axios) y [element-ui](https://github.com/ElemeFE/element), todos los datos de solicitud se simulan utilizando [Mock.js](https://github.com/nuysoft/Mock).
Comprender y aprender este conocimiento de antemano te será de gran ayuda para el uso de este proyecto.
Al mismo tiempo, apoyando una serie de artículos tutoriales, cómo construir un proyecto de fondo completo desde cero, te sugerimos que leas estos artículos y luego vengas a practicar este proyecto. Pero todavía no hay una versión en inglés.
- [Las manos tocan tu mano, usa tu vue 撸 Backstage Series 1 (Basic)](https://juejin.im/post/59097cd7a22b9d0065fb61d2)
- [Las manos tocan tu mano, usa tu vue 撸 Backstage Series 2 (Permisos de inicio de sesión)](https://juejin.im/post/591aa14f570c35006961acac)
- [Las manos tocan tu mano, usa tu vue 撸 Backstage Series III (combate real)](https://juejin.im/post/593121aa0ce4630057f70d35)
- [Toque de mano, llévese con vue 撸 Backstage Series 4 (vueAdmin, una plantilla de fondo minimalista)](https://juejin.im/post/595b4d776fb9a06bbe7dba56)
- [Toque con la mano, lo llevará a envolver un componente de vue](https://segmentfault.com/a/1190000009090836)
- [Manos y manos, traiga el icono de uso elegante](https://juejin.im/post/59bb864b5188257e7a427c09)
- [Las manos tocan tu mano, te usan con una postura razonable usando webpack4 (activado)](https://juejin.im/post/5b56909a518825195f499806)
- [Las manos tocan tu mano, te usan con una postura razonable usando webpack4 (abajo)](https://juejin.im/post/5b5d6d6f6fb9a04fea58aabc)
::: tip
**Este proyecto no es compatible con navegadores de bajo nivel (como IE). Si lo necesitas, agrega polyfills tu mismo.**
:::
## Estructura del proyecto
Este proyecto ha incorporado las siguientes plantillas, y han construido un andamiaje basado en Vue, que debería ayudarte a crear prototipos de interfaces de administración listas para producción. Cubre casi todo lo que necesitas.
```bash
├── build # construir archivos de configuración
├── mock # datos simulados
├── plop-templates # plantilla básica
├── public # activos estáticos puros (directamente copiados)
│ │── favicon.ico # favicon
│   └── index.html # plantilla index.html
├── src # código fuente principal
│   ├── api # servicio de api
│   ├── assets # activos del módulo como fuentes, imágenes (procesadas por webpack).
│   ├── components # componentes globales
│   ├── directive # directiva global
│   ├── filters # filtro global
│   ├── icons # iconos svg
│   ├── lang # idioma i18n
│   ├── layout # diseño global
│   ├── router # enrutador
│   ├── store # almacén
│   ├── styles # css global
│   ├── utils # utiles globales
│   ├── vendor # vendor
│   ├── views # vistas
│   ├── App.vue # componente principal de la aplicación
│   ├── main.js # archivo de entrada de la aplicación
│ └── permission.js # autenticación de permisos
├── tests # pruebas
├── .env.xxx # configuración de variables env
├── .eslintrc.js # configuración eslint
├── .babelrc # configuración babel
├── .travis.yml # configuración automatizada de CI
├── vue.config.js # configuración vue-cli
├── postcss.config.js # configuración postcss
└── package.json # package.json
```
## Empezando
```bash
# clonar el proyecto
git clone https://github.com/adempiere/adempiere-vue.git
# entrar en el directorio del proyecto
cd adempiere-vue
# instalar dependencias
npm install
# develop
npm run dev
```
<br/>
Esto se abrirá automáticamente [http://localhost:9527](http://localhost:9527).
Si ves la siguiente página, entonces has tenido éxito.
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/1bc334a6-32a8-4f29-a037-ac3f5ce32588.png)
Tenemos modelos integrados, componentes estándar, datos simulados, recarga módulos hot, administración de estado, i18n, enrutador global, etc. Puedes continuar explorando otros documentos para obtener más detalles sobre esos temas.
<br/>
::: tip
**Sugerencia:** Puedes usar `adempiere-vue` como una caja de herramientas o como un repositorio de solución de integración, se recomienda hacer un desarrollo secundario sobre la base de `vue-admin-template`, si necesitas alguna característica adicional, la puedes copiar desde `adempiere-vue`.
:::
## Contribución
El repositorio de documentación es [adempiere-vue-site](https://github.com/erpcya/adempiere-vue-site) basado en el desarrollo [vuepress](https://github.com/vuejs/vuepress).
Puede haber algunos errores ortográficos o de traducción al escribir este documento. Eres bienvenido a señalar por issue o por pr.
[adempiere-vue](https://github.com/adempiere/adempiere-vue) también continúa iterando, resumiendo y resumiendo más funciones, y resume las mejores prácticas de los escenarios de productos templates/components/business de negocio en el medio y de back office. Este proyecto también espera tu participación y [comentarios](https://github.com/adempiere/adempiere-vue/issues).
## Donar
Si encuentras útil este proyecto, puedes comprar un vaso de jugo para el autor :heart:
[Donar](/donate/)
## Soporte de navegadores
Navegadores modernos e Internet Explorer 10+.
<!-- prettier-ignore -->
| [<img class="no-margin" src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img class="no-margin" src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img class="no-margin" src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img class="no-margin" src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
| --------- | --------- | --------- | --------- |
| IE10, IE11, Edge| últimas 2 versiones | últimas 2 versiones | últimas 2 versiones
## Ecosistema Vue
**En primer lugar, comprender las cosas en estos ecosistemas vue te ayudará a comenzar con este proyecto.**
1. [Vue Router](https://router.vuejs.org/) Vue Router es el enrutador oficial de Vue.js. Se integra profundamente con el núcleo de Vue.js para facilitar la creación de Aplicaciones de una sola página con Vue.js.
2. [Vuex](https://vuex.vuejs.org/) Vuex es un patrón de gestión de estado + biblioteca para aplicaciones Vue.js. Sirve como un almacén centralizado para todos los componentes de una aplicación, con reglas que aseguran que el estado solo pueda mutarse de manera predecible.
3. [Vue Loader](https://vue-loader.vuejs.org) Vue-loader es un cargador para webpack que permite crear componentes de Vue en un formato llamado Componentes de un solo archivo (SFCs). La combinación de webpack y vue-loader brinda un flujo de trabajo front-end moderno, flexible y extremadamente potente para la creación de aplicaciones Vue.js.
4. [Vue Server Renderer](https://ssr.vuejs.org/) Vue-server-renderer facilita la creación de aplicaciones JavaScript isomorfas o universales que se ejecutan tanto en el lado del servidor como del cliente, donde la mayoría del código de la aplicación se comparte y se reutiliza.
5. [Vue Test Utils](https://vue-test-utils.vuejs.org/) Vue Test Utils es la biblioteca de utilidad de prueba de unidad oficial para Vue.js.
6. [Vue Dev-Tools](https://github.com/vuejs/vue-devtools) Extensión de navegador devtools para depurar aplicaciones Vue.js.
7. [Vue CLI](https://cli.vuejs.org/) Vue CLI es un sistema completo para el rápido desarrollo de Vue.js. Su objetivo es ser la línea base de herramientas estándar para el ecosistema Vue. Asegura que las diversas herramientas de compilación funcionen sin problemas junto con valores predeterminados razonables para que pueda concentrarse en escribir tu aplicación en lugar de pasar días discutiendo con las configuraciones.
8. [Vetur](https://github.com/vuejs/vetur) Herramientas Vue para VS Code. Escribe vue essential plugins en VS Code.

View File

@ -0,0 +1,80 @@
# CDN
You can analyze the results of the `webpack` package by executing `npm run preview -- --report` and observe the size of each static resource. You can find that the most occupied space is the dependence of third parties. Such as `vue`, `element-ui`, `ECharts`, etc.
You can use the `CDN` link to introduce these third-party libraries, which can greatly increase the speed of the build (the resources introduced through the CDN are not packaged by webpack). If your project does not have its own `CDN` service, use some third-party `CDN` services, such as [unpkg](https://unpkg.com/), etc. It is a good choice, it has provided free Resource acceleration. At the same time, it provides cache optimization. Since your third-party resources are introduced in `html` through `script`, its cache update strategy is controlled by you manually, eliminating the need to optimize the cache strategy.
::: tip
Many articles say that the use of `CDN` can greatly reduce the size of the code, which is impossible. Although the packaged `bundle` is small. But that part of the code was just removed by you, and it was introduced using the `CDN` method. The most efficient solution you want to reduce the size is to enable `GZIP`.
:::
## I personally do not use `CDN`
There is no problem with the temporary build speed, and there is no need to strip some of the third-party dependencies separately. Using `CDN` equals some third-party dependent versions you control through `package.json`, some dependencies require manual maintenance, adding some maintenance costs. At present, the webpack-based `optimization.splitChunks` has been optimized for the caching of resources, and the caching of static resources has been done very well. And all current static resources will be uploaded to their own `CDN` service, there is no need to use a third-party `CDN` service.
**Of course, all optimizations need to be adjusted in conjunction with their specific business!** If you feel that the use of `CDN` is beneficial for your project, you can follow these steps:
## Way of use
First find `vue.config.js`, add `externals` to make `webpack` not package `vue` and `element`
```js
externals: {
vue: 'Vue',
'element-ui':'ELEMENT'
}
```
Then configure the `CDN` of those third-party resources, please pay attention to the order.
```js
const cdn = {
css: [
// element-ui css
'https://unpkg.com/element-ui/lib/theme-chalk/index.css'
],
js: [
// vue must at first!
'https://unpkg.com/vue/dist/vue.js',
// element-ui js
'https://unpkg.com/element-ui/lib/index.js'
]
}
```
Then inject it into `index.html` via `html-webpack-plugin`:
```js
config.plugin('html').tap(args => {
args[0].cdn = cdn
return args
})
```
Find `public/index.html`. Inject css and js in turn through your configured `CND Config`.
```html
<head>
<!-- inject css-->
<% for(var css of htmlWebpackPlugin.options.cdn.css) { %>
<link rel="stylesheet" href="<%=css%>">
<% } %>
</head>
<!-- inject js -->
<% for(var js of htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%=js%>"></script>
<% } %>
```
There is also a small detail. If you use the global object method to introduce vue, you don't need to manually Vue.use(Vuex), it will be mounted automatically. [issue](https://github.com/vuejs/vuex/issues/731)
Complete [code modification](https://github.com/adempiere/vue-admin-template/commit/eaaa3c1ddadd114451a1a83e042f1fc56a9809a1)
Finally you can use `npm run preview -- --report` to see the effect as shown:
![](https://camo.githubusercontent.com/0c5bdc47aeaecc340b9a5a88325b49885538bf90/68747470733a2f2f70616e6a69616368656e2e6769746875622e696f2f696d616765732f656c656d656e742d63646e2e706e67)
::: tip
By the same token, other third-party dependencies can be handled in the same way(such as `vuex`, `vue-router`, etc.). Of course, you can also choose to use [DLLPlugin](https://webpack.docschina.org/plugins/dll-plugin/) to handle third-party dependencies to optimize the build.
:::

133
docs/es/guide/advanced/chart.md Executable file
View File

@ -0,0 +1,133 @@
# Chart
Managing background charts is also a common requirement. The chart here only recommends ECharts, full-featured, community demo is also rich [gallery](http://gallery.echartsjs.com/explore.html)。
I still have that point of view. Most plug-ins recommend that use vue for packaging by yourself. It's really simple. ECharts supports the import of webpack, you can import the whole ECharts `var echarts = require ('echarts')` However, ECharts is not small, if you use only a small part of the features or chart type, then recommend on-demand import.
```js
// Import on demand -- import ECharts main module
var echarts = require('echarts/lib/echarts')
// Import bar
require('echarts/lib/chart/bar')
// Import tooltip&title
require('echarts/lib/component/tooltip')
require('echarts/lib/component/title')
// Import all ECharts module
var echarts = require('echarts')
```
[Use ECharts with webpack](https://ecomfe.github.io/echarts-doc/public/en/tutorial.html#Use%20ECharts%20with%20webpack)
[Include ECharts charts and components on demand](https://ecomfe.github.io/echarts-doc/public/en/tutorial.html#Use%20ECharts%20with%20webpack)
Next we will declare the initialization of ECharts in vue. Because ECharts initialization must be bound to dom, we can only initialize it during vue's mounted lifetime.
```js
mounted() {
this.initCharts();
},
methods: {
initCharts() {
this.chart = echarts.init(this.$el);
this.setOptions();
},
setOptions() {
this.chart.setOption({
title: {
text: 'ECharts 入门示例'
},
tooltip: {},
xAxis: {
data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"]
},
yAxis: {},
series: [{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}]
})
}
}
```
It's that simple, ECharts is configured, at this point you want to say that my data is obtained remotely, or how do I dynamically change the configuration of ECharts? We can trigger the setOptions method with watch
```js
// The first watch options change Using the depth of vue watcher, options are re-setOption
watch: {
options: {
handler(options) {
this.chart.setOption(this.options)
},
deep: true
},
}
// The second only watch data changes trigger ECharts only when the data changes
watch: {
seriesData(val) {
this.setOptions({series:val})
}
}
```
In fact, they are all similar, or they must be combined with their own business. There is no difference between using ECharts in peacetime.
## Demo
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/137aeadd-ad0e-4b21-badd-c53f96b7482b.gif)
::: tip Code
`@/views/dashboard/admin/components`
:::
## ECharts chart width is displayed incorrectly?
Sometimes you put ECharts in `el-tab` or`el-dialog`, and you will find that the width of the chart will be displayed incorrectly. As shown below:
<img :src="$withBase('/images/ECharts-width.png')" alt="ECharts-width.png" width="500px">
Because ECharts itself is not adaptive, you need to manually call its `.resize ()` method when the width of your parent container changes.
For example, `el-tab`, you can listen to the`change` event, and call the `.resize ()` method after finding the chart when the change occurs.
```html
<template>
<el-tabs v-model="active" @tab-click="handleClick">
<el-tab-pane label="用户管理" name="first">
<Chart ref="Chart" />
</el-tab-pane>
<el-tab-pane label="配置管理" name="second">配置管理</el-tab-pane>
<el-tab-pane label="角色管理" name="third">角色管理</el-tab-pane>
<el-tab-pane label="定时任务补偿" name="fourth">定时任务补偿</el-tab-pane>
</el-tabs>
</template>
<script>
export default {
data() {
return {
active: 'second'
};
},
watch: {
active(val) {
this.$nextTick(() => {
this.$refs.Chart.resize();
}
}
},
methods: {
handleClick(tab, event) {
console.log(tab, event);
}
}
};
</script>
```
It is relatively simple to put the chart in the `el-dialog`, as long as the init chart is displayed after the dialog appears.
## Others
Of course there are many other libraries in the community, such as [d3](https://github.com/d3/d3) , [Chart.js](https://github.com/chartjs/Chart.js) , [chartist-js](https://github.com/gionkunz/chartist-js). The packaging methods are almost the same, and they are no longer here.

20
docs/es/guide/advanced/cors.md Executable file
View File

@ -0,0 +1,20 @@
# Cors
The most question be asked is still about `cross-domain` issues. In fact, the `cross-domain` issue is really not a very difficult question to solve. Here I will briefly summarize several `cross-domain` solutions I recommend.
The most recommended way is `cors`, full name is `Cross Origin Resource Sharing`. This solution does not make any difference to the front-end write request as usual. The workload is basically on the back-end. For each request, the browser must first send a pre-request as `OPTIONS`, to know the server-side HTTP method supported for cross-source requests. After confirming that the server allows the cross-source request, then send the real request with the actual HTTP request method. Details [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)
The recommended reason is: as long as the first time is configured, no matter how many API interfaces and projects, they can be directly reused, and the cross-domain problem can be solved once and for all, and it can be conveniently used in both the development environment and the formal environment.
But there are always some back-end developers who think `cors` is too much `trouble`, they don't want to help the front end to solve cross-domain issues. That pure front-end is also has solutions.
In `dev` environment, you can use webpack `proxy`, it is also very easy to use。 It's recommended that you look at the [document](https://webpack.js.org/configuration/dev-server/#devserverproxy) and we're not going to discuss it here. Some of the author's personal projects use this method
But this method can not used in the `production` environment. In `production` environment, you need to use `nginx` reverse proxy. Whether `proxy` or `nginx`, the principle is the same. Solve the cross-domain issues by building a transit server to forward requests.
| development | production |
| :---------: | ---------- |
| cors | cors |
| proxy | nginx |
Here I only recommend these two ways to cross-domain, there are many other cross-domain methods but not recommended.

98
docs/es/guide/advanced/error.md Executable file
View File

@ -0,0 +1,98 @@
# Error Handling
## Page
**404**
Page-level error handling is handled uniformly by the `vue-router`. All pages that do not match the correct route will advance to the `404` page.
```js
{ path: '*', redirect: '/404' }
```
::: warning
One thing that needs special attention here is that the `404` page must be loaded last. If you put `404` in the constantRoutes , then the following page will be blocked to `404`. See the problem for details [addRoutes when you've got a wildcard route for 404s does not work](https://github.com/vuejs/vue-router/issues/1176)
:::
**401**
Permission control is done in `@/permission.js`. All users who do not have permission to access this route will be redirected to the `401` page.
<br/>
## Request
All the requests in the project will go through the axios instance created in `@/utils/request.js`. [code](https://github.com/adempiere/adempiere-vue/blob/master/src/utils/request.js)。
You can use the `service.interceptors.response`, the response interceptor to harmonize different status codes according to your actual business or to perform error handling according to custom code. Such as:
```js
service.interceptors.response.use(
response => {
/**
* The code is non-20000 error-free
*/
const res = response.data
if (res.code !== 20000) {
Message({
message: res.data,
type: 'error',
duration: 5 * 1000
})
// 50008: illegal token; 50012: other client logged in; 50014: Token expired;
if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
MessageBox.confirm(
'你已被登出,可以取消继续留在该页面,或者重新登录',
'确定登出',
{
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
store.dispatch('FedLogOut').then(() => {
location.reload() // 为了重新实例化vue-router对象 避免bug
})
})
}
return Promise.reject('error')
} else {
return response.data
}
},
error => {
console.log('err' + error) // for debug
Message({
message: error.message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
```
Since all requests return a `promise`, you can also pass a `catch` error for each request, which allows for separate processing.
```js
getInfo()
.then(res => {})
.catch(err => {
xxxx
})
```
## Coding
This project also does code-level error handling. If you enable `eslint`, you will be prompted for errors when writing code. Such as:
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/b037f47c-1f7b-487f-bb05-32e7300767d2.png)
Of course there are many errors that cannot be checked by `eslint`, vue also provides global error handling hooks[errorHandler](https://vuejs.org/v2/api/#errorHandler). The project also made a corresponding error collection.
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/360e4842-4db5-42d0-b078-f9a84a825546.gif)
::: tip
Listening error: [@/errorLog.js](https://github.com/adempiere/adempiere-vue/blob/master/src/errorLog.js)
Error display component: [@/components/ErrorLog](https://github.com/adempiere/adempiere-vue/blob/master/src/components/ErrorLog/index.vue)
:::

View File

@ -0,0 +1,77 @@
# ESLint
Whether it's a multi-person collaboration or personal projects, code specifications are important. It can not only avoids basic syntax errors, but also ensures the readability of the code.
## Config
All configuration files are in [.eslintrc.js](https://github.com/adempiere/adempiere-vue/blob/master/.eslintrc.js).
The basic eslint rules of this project is based on the official eslint rules of vue [eslint-config-vue](https://github.com/vuejs/eslint-config-vue) but made minor changes. You can customize your configuration according to your needs.
Such as: my personal or project team is accustomed to using two spaces, but you may feel that the four spaces are more pleasing, and you can make the following changes.
Enter the project of `.eslintrc.js`, find `indent`,and then set it to `4` 。There are a variety of configuration information, see details [ESLint Document](https://eslint.org/docs/rules/)。
After [v3.8.1](https://github.com/adempiere/adempiere-vue/releases/tag/v3.8.1), [eslint-plugin-vue](https://github.Com/vuejs/eslint-plugin-vue) has been added to better verify vue related code.
By default, the most restrictive config `plugin:vue/recommended` is used to verify the code. If you think it is too strict, you can modify it yourself.
```js
// https://github.com/adempiere/adempiere-vue/blob/master/.eslintrc.js
module.exports = {
extends: ['plugin:vue/recommended', 'eslint:recommended']
//You can change it to extends: ['plugin:vue/essential', 'eslint:recommended']
}
```
## Cancel ESLint
If you don't want to use ESLint (not recommended for cancellation), just find the [vue.config.js](https://github.com/adempiere/adempiere-vue/blob/master/vue.config.js) file.
Make the following settings `lintOnSave: false`.
## Configure ESLint in vscode
Sharp tools make good work! Personally recommend eslint+vscode to write VUE, there is definitely a very cool
![eslintGif.gif](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/e94a76df-6dc0-4c15-9785-28b553a163e9.png)
<br/>
Every time you save your code, vscode will be able to mark red areas that do not conform to the eslint rules, and make some simple self-fixes at the same time. The installation steps are as follows:
First install the eslint plugin
![eslint1.png](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/72f126cb-09eb-4b27-b02e-65e79eb76220.png)
After we have installed ESLint, we back to VSCode to set up . Go to `Code` > `Preferences` > `Settings` and add the following configuration.
```json
{
"files.autoSave": "off",
"eslint.validate": [
"javascript",
"javascriptreact",
"vue-html",
{
"language": "vue",
"autoFix": true
}
],
"eslint.run": "onSave",
"eslint.autoFixOnSave": true
}
```
Everyone and the team have their own code specification, unification is good, to create their own eslint rules and upload it to the npm will be fun. Such as ElemeFE [config](https://www.npmjs.com/package/eslint-config-elemefe) or Vue official [config](https://github.com/vuejs/eslint-config-vue).
[vscode plugin and configuration recommendations](https://github.com/varHarrie/Dawn-Blossoms/issues/10)
## More configuration
Since this project is built based on `vue-cli`, more configuration can be found in the official [documentation](https://cli.vuejs.org/en/config/#lintonsave)
## Auto fix
```bash
npm run lint -- --fix
```
Running the above command, eslint will automatically fix some simple errors.

View File

@ -0,0 +1,78 @@
# Git Hooks
Programmers with engineering literacy will pay attention to coding standards, and Code Linting (Lint) is an important means to ensure code specification and consistency.
What are the benefits of using `Lint`? In my opinion, it has at least the following three points:
- Fewer bugs
- With higher development efficiency, Lint can easily find low-level, obvious errors.
- Higher code readability
Many times our `lint` check is placed in the continuous integration phase, the approximate process is as follows:
> Code Submission --> Run CI Found Problem (Remote) --> Local Fix Issue --> Resubmit --> Pass Check (Remote)
But there is a problem with this. Our `CI` (continuous integration) often doesn't just do `Lint` work, it also has many other tasks (such as packaging files, static resources uploaded to CDN, etc.), which leads to It's a special waste of time, it may take a few minutes for you to find the problem, or sometimes you don't find your `CI` is failed.
Common process: write the code locally, submit, start running lint, find that it does not pass, modify the code locally, submit it, wait for the result of CI, and repeat the previous operation if there are any problems.
## husky
The most effective solution is to put the `Lint` checksum locally. The common practice is to use [husky](https://github.com/typicode/husky) or [pre-commit](https://github.com /observing/pre-commit) Do a `Lint` check before committing locally.
> Of course, if you use `vue-cli@3` when creating your project, you can also use its built-in [yorkie](https://github.com/yyx990803/yorkie), which is based on `husky`, but Changed the interface. But here we still use `husky` as an example.
```bash
# Note: Our examples are all 1.3.1+ versions!
npm install husky -D -S
```
Then modify `package.json` to add the configuration:
```json
"husky": {
"hooks": {
"pre-commit": "eslint --ext .js,.vue src"
}
}
```
Finally try the `Git` submission and you will receive feedback soon:
```
git commit -m "Keep calm and commit"
```
But there is a problem. In my this git submission, I may have only modified one file. For example, I modified the content of `foo.js`, but it will still check all the '.js' files in `src`. It is very unfriendly. Every time I submit the code I wrote, I have to solve the other person's code lint problem first, then I can submit the code smoothly, and when the project is big, the inspection speed will become more and more slow.
## lint-staged
To solve the pain points above, you need to use [lint-staged](https://github.com/okonet/lint-staged). It will only check to check what you submitted or what you modified.
```bash
npm install lint-staged -D -S
```
Then, modify the package.json configuration:
```json
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"src/**/*.{js,vue}": [
"eslint --fix",
"git add"
]
}
```
As configured above, each time it will only check your local configuration for the `eslint` rule (this see the document [ESLint](eslint.md)) before your local `commit`, if it meets the rules, it will be submitted successfully. If it does not match, it will automatically execute `eslint --fix` to try to help you fix it automatically. If the repair is successful, it will help you to submit the repaired code. If it fails, you will be prompted with an error, and you will be allowed to submit the code only after you fix it.
## To sum up
The best `lint` specification process is to recommend team members to configure `eslint` in their own editor, and turn on the `eslint-loader` error in webpack, so the editor can help you automatically fixed some simple errors when you write. At the same time, it can obviously remind you of the code that does not meet the `lint` specification. See [ESLint](eslint.md) for details on this.
But this is not mandatory. Some team members or newly arrived interns have not configured the lint rule in the editor or ignored the error in the command line. In this case, you need to configure the mandatory pre-commit. Check that everything submitted to the remote repository is in compliance with the team's specifications.

96
docs/es/guide/advanced/i18n.md Executable file
View File

@ -0,0 +1,96 @@
# I18n
This project is a collection of internationalized i18n solutions. Implemented via [vue-i18n](https://github.com/kazupon/vue-i18n).
Since the project's ui framework uses `element`, internationalization also needs to be internationalized.
[code](https://github.com/adempiere/adempiere-vue/blob/master/src/lang/index.js).
At the same time, the current `lang` language save in the `cookie`, and the last language setting can be remembered for opening the page next time.
## Global lang
Code: [@/lang](https://github.com/adempiere/adempiere-vue/tree/master/src/lang)
Currently set English and Chinese languages.
Meanwhile, import a language package in `@/lang/index.js` for `element-ui`.
## Async lang
There are some langs that are needed for specific pages, such as the `@/views/i18n` page, you can use async lang.
```js
import local from './local'
this.$i18n.mergeLocaleMessage('en', local.en)
this.$i18n.mergeLocaleMessage('zh', local.zh)
```
# Use $t in js
If you use a component such as `select`, its value comes through `v-for`, such as:
```html
<el-select v-model="value">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"/>
</el-select>
```
```js
this.options = [
{
value: '1',
label: this.$t('i18nView.one')
},
{
value: '2',
label: this.$t('i18nView.two')
},
{
value: '3',
label: this.$t('i18nView.three')
}
]
```
In this case, i18n will only be executed once, because `this.options` in js will only be executed once during `created`, and its data will not change as your local `lang` changes, so You need to manually reset `this.options` when the `lang` changes.
```js
export default {
watch: {
lang() {
this.setOptions()
}
},
methods: {
setOptions() {
this.options = [
{
value: '1',
label: this.$t('i18nView.one')
},
{
value: '2',
label: this.$t('i18nView.two')
},
{
value: '3',
label: this.$t('i18nView.three')
}
]
}
}
}
```
## Remove i18n
In `src/main.js` remove `import i18n from './lang'` and delete `src/lang` folder.
And remove `this.$t('route.xxxx')` in `src/layout/components/Levelbar``src/layout/components/SidebarItem``src/layout/components/TabsView` or others.
After the <Badge text="v4.1.0+"/> version, the default master will no longer provide i18n. Because most users are not need i18n, the removal of i18n workload is quite large.
If you have i18n requirements, please use [i18n Branch](https://github.com/adempiere/adempiere-vue/tree/i18n), which is updated synchronously with master.

49
docs/es/guide/advanced/icon.md Executable file
View File

@ -0,0 +1,49 @@
# Icon
If you do not find the desired icon in the [Icon](https://github.com/adempiere/adempiere-vue/tree/master/src/icons/svg) of this project, you can select and generate your own business icon library on [iconfont.cn](http://iconfont.cn/)and use it again. Or other svg icon website, download svg and put it in this folder.
## Generate icon library code
First, search for and find the icon you need, and collect it into your shopping cart. In the shopping cart, you can add the selected icon to the project (if not, create a new one), and the subsequent generated resources/code are It is based on the dimension of the project.
> If you already have a design draft, just need to generate the relevant code, you can upload your icon, and then do the above operation.
<img width="600" alt="账户相关布局" src="https://gw.alipayobjects.com/zos/rmsportal/jJQYzRyqVFBBamUOppXH.png" />
<br />
** This project now supports and recommends separate export of svg usage. Download method as shown below:**
<img width="600" src="https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/1f8b1e56-cfd9-4ef7-a0aa-dfb0c2883aa3.gif" />
<br />
After the download is complete, the downloaded .svg file is automatically imported after it is placed in the `@/icons/svg` folder.
## How to use
```js
<svg-icon icon-class="password" /> // icon-class is the icon's name usage
```
[Component](/feature/component/svg-icon.md)
## Change color
`svg-icon` reads its parent's color `fill: currentColor;' by default.
You can change the parent's `color` or change the color of `fill` directly.
:::tip
If you encounter an incorrect icon color, you can refer to this[issue](https://github.com/adempiere/adempiere-vue/issues/330)
:::
## Detailed articles
[手摸手,带你优雅的使用 icon](https://juejin.im/post/59bb864b5188257e7a427c09)
## Currently available icons
[src/icons/svg](https://github.com/adempiere/adempiere-vue/tree/master/src/icons/svg)
Online [Preview Address](https://adempiere.github.io/adempiere-vue/#/icon/index)

View File

@ -0,0 +1,112 @@
# Lazy Loading Routes
When you package an application, the Javascript package becomes very large, affecting the page load. If we can split the components corresponding to different routes into different code blocks and then load the corresponding components when the route is accessed, this will be more efficient.
Combining Vue's [async component feature](https://vuejs.org/v2/guide/components-dynamic-async.html#Async-Components) and webpack's [code splitting feature](https://webpack.js.org/guides/code-splitting/), it's trivially easy to lazy-load route components.
```js
const Foo = () => import('./Foo.vue')
```
<br>
**When you think your page's hot reload is slow, you need to look down ↓**
## Differentiating development and production environments
**[This solution has been eliminated]**
When you have more and more pages in your project, using `lazy-loading` in the development environment becomes less appropriate, and every change of code that triggers a hot update becomes very slow. Therefore, it is recommended to only use the lazy loading function in the build environment.
**Development:**
```js
// vue-loader at least v13.0.0+
module.exports = file => require('@/views/' + file + '.vue').default
```
**Note here that this method only supports `vue-loader at least v13.0.0+`**[adempiere-vue/issues/231](https://github.com/adempiere/adempiere-vue/issues/231)
Production
```js
module.exports = file => () => import('@/views/' + file + '.vue')
```
## Elimination reason
Of course, there are some side effects of writing this way. due to
> Every module that could potentially be requested on an import() call is included. For example, import(./locale/${language}.json) will cause every .json file in the ./locale directory to be bundled into the new chunk. At run time, when the variable language has been computed, any file like english.json or german.json will be available for consumption.
::: tip
The user can measure whether to adopt this method according to the business situation. If your project is not large and you can also accept the local development hot update speed. You can continue to use lazy loading to avoid this side effect in all environments.
:::
## New Plan
Use `babel plugins` [babel-plugin-dynamic-import-node](https://github.com/airbnb/babel-plugin-dynamic-import-node).
It only does one thing by converting all `import()` to `require()`, so that all asynchronous components can be import synchronously using this plugin. Combined with the babel environment variable [BABEL_ENV](https://babeljs.io/docs/usage/babelrc/#env-option), let it only work in the development environment, in the development environment will convert all import () into require ().
This solution to solve the problem of repeated packaging before, while the invasiveness of the code is also very small, you usually write routing only need to follow the lazy loading method of the [official document](https://router.vuejs.org/guide/advanced/lazy-loading.html) routing on it, the other are handed to the handle of the cable, When you don't want to use this program, just remove it from Babel's plugins.
**Code:**
First add `BABEL_ENV` to `package.json`
```json
"dev": "cross-env BABEL_ENV=development webpack-dev-server --inline --progress --config build/webpack.dev.conf.js"
```
Then `.babelrc` can only include the `babel-plugin-dynamic-import-node` `plugins` and make it work only in the `development` mode.
```json
{
"env": {
"development": {
"plugins": ["dynamic-import-node"]
}
}
}
```
After that, you're done. Routing can be written as usual.
```js
{ path: '/login', component: () => import('@/views/login/index')}
```
[Related code changes](https://github.com/adempiere/adempiere-vue/pull/727)
## vue-cli@3 [The plan has been eliminated]
`adempiere-vue@4` has been modified to build based on `vue-cli` in the new version. So in the new version you just need to set `VUE_CLI_BABEL_TRANSPILE_MODULES:true` in the `.env.development` environment variable configuration file, specifically [code](https://github.com/adempiere/adempiere-vue/blob/master/.env.development).
Its implementation logic and principle are the same as before, it based on `babel-plugin-dynamic-import-node`.The only thing you need to set a variable in `vue-cli` is to borrow the default configuration of `vue-cli`. By reading [source code](https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/babel-preset-app/index.js), `vue-cli` will pass `VUE_CLI_BABEL_TRANSPILE_MODULES`,this environment variable to distinguish whether to use `babel-plugin-dynamic-import-node`, so we only need to set it to true. Although its original intention was for unit testing, it just met our needs.
### Elimination reason
In the era of `vue-cli@3`, using `VUE_CLI_BABEL_TRANSPILE_MODULES` is ok, but it is actually fragile, as in `vue-cli@4`, vue-cli introduces `babel-plugin-dynamic-import-node The logic of`has changed, it needs to be `VUE_CLI_BABEL_TRANSPILE_MODULES` and `VUE_CLI_BABEL_TARGET_NODE` to be true at the same time, so as long as the judgment logic of vue-cli changes, we need to make corresponding changes, or be very passive and coupled . So in the `vue-cli@4` version, we no longer set it by `VUE_CLI_BABEL_TRANSPILE_MODULES: true`, but by manually introducing `'babel-plugin-dynamic-import-node'`, see the next section for details.
## vue-cli@4
1. No need to configure `VUE_CLI_BABEL_TRANSPILE_MODULES = true` in the `.env.development` file, just delete it.
2. Run `npm install babel-plugin-dynamic-import-node -S -D`
3. The way to add the dynamic-import-node plugin in `babel.config.js`, see the next section for details.
```js
module.exports = {
presets: ['@vue/cli-plugin-babel/preset'],
env: {
development: {
plugins: ['dynamic-import-node']
}
}
}
```
## Improve
`webpack5` is about to be released, greatly improving the speed of packaging and compiling. After that, it may not need to be so complicated at all. More page hot updates can be very fast, and the solution mentioned above is completely unnecessary.

View File

@ -0,0 +1,56 @@
# Node Sass to Dart Sass
Before `v4.3.0`, this project was built based on `node-sass`, but `node-sass` low-level dependencies [libsass](https://github.com/sass/libsass), resulting in many users installing Especially difficult for Windows users, it forces users to install `python2` and `Visual Studio` in the `windows` environment to compile successfully.
So in order to solve this problem, this project was modified to build `dart-sass` in [v4.3.0](https://github.com/adempiere/adempiere-vue/pull/3040), it can guarantee performance Under the premise of greatly simplifying the user's installation costs. Through this [issue](https://github.com/adempiere/adempiere-vue/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc) the relevant comments below can be known, install` Node-sass is such a troublesome thing.
There is a more important reason for choosing to use dart-sass here. Officially, `sass` has taken dart-sass as the main development direction in the future. Any new features will be supported first, and it It has been running steadily in the community for a long time, and there are basically no pits. The main reason why dart-sass is easy to install is because it will be compiled into pure js, so that it can be used directly in the node environment. Although its running speed will be slower than that based on [libsass](https://github.com/sass/libsass), the difference in these speeds is almost negligible. The entire community is now embracing `dart-sass`, and we have no reason to refuse! And it does greatly simplify the user's installation costs.
Currently, `vue-cli` will also prefer to use `dart-scss` by default when selecting `sass` preprocessing, related: [pr](https://github.com/vuejs/vue-cli/pull/3321)
Related instructions can be found in this article: [Announcing Dart Sass](https://sass-lang.com/blog/announcing-dart-sass)
Specific `dart-sass` performance evaluation can be seen: [Perf Report](https://github.com/sass/dart-sass/blob/master/perf.md)
## Upgrade plan
The upgrade is also very simple, requiring only two steps
```bash
npm uninstall node-sass
npm install sass -S -D
```
The upgrade can also be seen in detail: [Pull Request](https://github.com/adempiere/adempiere-vue/pull/3040) is simple and only requires two steps
## Not compatible
One thing to note after replacing `node-sass` is that it no longer supports the `/deep/` writing style of `sass` before, and it needs to be changed to the writing style of `::v-deep`. Related: [issue](https://github.com/vuejs/vue-cli/issues/3399)
Concrete demo:
```css
.a {
/deep/ {
.b {
color: red;
}
}
}
/* change into */
.a {
::v-deep {
.b {
color: red;
}
}
}
```
Regardless of whether you use `dart-sass` or not, I suggest you use `::v-deep` notation, which is not only compatible with the css `>>>` notation, but also compatible with sass `/deep/` . And it's the way of writing specified in [vue 3.0 RFC](https://github.com/vuejs/rfcs/blob/scoped-styles-changes/active-rfcs/0023-scoped-styles-changes.md).
And the original writing of `/deep/` itself was abandoned by Chrome. You can often find a warning in the console that Chrome reminds you not to use `/deep/`.
More: [scope css writing](https://vue-loader.vuejs.org/guide/scoped-css.html)

View File

@ -0,0 +1,43 @@
## Style Guide
The style guide for this project is mainly based on the official [style guide](https://vuejs.org/v2/style-guide/index.html) . It is recommended to read the guide before you start using the project, which will help you write more standardized and unified code. Most of these rules are also configured in [eslint-plugin-vue](https://github.com/vuejs/eslint-plugin-vue). When the rules are not followed, will throw an error. For details, see [eslint](./eslint.md) section.
Of course, there are some special specifications that cannot be verified by eslint. You need to pay attention to yourself and follow. The most important thing is the naming rules for files. Take the example of `adempiere-vue` here.
## Component
All `Component` files start with uppercase (PascalCase), which is also official [recommended](https://vuejs.org/v2/style-guide/index.html#Single-file-component-filename-casing-strongly-recommended)。
But except for `index.vue`.
Example:
- `@/components/BackToTop/index.vue`
- `@/components/Charts/Line.vue`
- `@/views/example/components/Button.vue`
## JS files
All `.js` files follow `kebab-case`.
Example:
- `@/utils/open-window.js`
- `@/views/svg-icons/require-icons.js`
- `@/components/MarkdownEditor/default-options.js`
## Views
Under the `views` file, the `.vue` files representing the routes path all use `kebab-case`, and the same rules are used for folders.
Example:
- `@/views/svg-icons/index.vue`
- `@/views/svg-icons/require-icons.js`
The use of a kebab-case to name `views` is mainly due to the following considerations.
- `kebab-case` is also one of the officially recommended naming conventions [Document](https://vuejs.org/v2/style-guide/index.html#Single-file-component-filename-casing-strongly-recommended)
- The `.vue` file under `views` represents a route, so it needs to be distinguished from `component` (components are `PascalCase`)
- The `url` of the page is follow `kebab-case` , such as `https://www.xxx.admin/export-excel`. So the `view` corresponding to the route should be kept uniform.
- No case sensitive issues

108
docs/es/guide/advanced/theme.md Executable file
View File

@ -0,0 +1,108 @@
# Theme
This project is based on the element-ui default visual style. If you have additional requirements for visual style, you can follow the official custom theme [guide](http://element.eleme.io/#/en-US/component/custom-theme). The method is implemented by covering style variables.
## Style override
The generic style variables for element-ui may not satisfy all custom requirements, and you can do this by overriding the default component style.Since the element-ui style is introduced globally, you can't add scoped if you want to override its style in a `view`, but if you want to override only the element style of the page, you can use it. Add a class to the parent to use the namespace to solve the problem.
Or use [Deep Selectors](https://vue-loader.vuejs.org/guide/scoped-css.html#deep-selectors)。
```css
/* Your namespace */
.article-page {
/* element-ui tag */
.el-tag {
margin-right: 0px;
}
}
```
Some global element-ui style modifications can be set in [@/styles/element-ui.scss](https://github.com/adempiere/adempiere-vue/blob/master/src/styles/element-ui.scss).
<br/>
## Dynamic theme
This project provides two kinds of dynamic skinning functions, each has its own advantages and disadvantages. Please choose according to your own needs.
### Element-ui official method
After the element-ui is upgraded to 2.0, the dynamic peel function is provided in the upper right corner of the official document. This project also provides a change function.
Code: [@/components/ThemePicker](https://github.com/adempiere/adempiere-vue/blob/master/src/components/ThemePicker/index.vue)。
**Briefly explain its principle:** All styles after element-ui version 2.0 are based on SCSS, all colors are set based on a few basic color [variables](https://github.com/adempiere/custom-element-theme/blob/master/element-variables.scss), so it is not difficult to achieve dynamic skinning, as long as find a few color variables to modify it. First, we need to get the version number of element-ui through `package.json` and request the corresponding style according to the version number. After you get the style, you will change the color, replace it with the color variable you want, and then dynamically add the `style` tag to override the original CSS style.
::: tip
It is necessary to obtain the version of element-ui to lock the version so as to avoid the impact of non-compatible updates when the Element is upgraded in the future.
:::
```js
const version = require('element-ui/package.json').version
const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`
this.getCSSString(url, chalkHandler, 'chalk')
getCSSString(url, callback, variable) {
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && xhr.status === 200) {
this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, '')
callback()
}
}
xhr.open('GET', url)
xhr.send()
}
```
**How to use**
Import the ThemePicker component to your project
```js
import ThemePicker from '@/components/ThemePicker'
```
- Advantage
- No need to prepare multiple sets of themes, free dynamic theme
- Shortcomings
- Not enough customization, only support switching of basic colors
<br/>
<br/>
### Multiple sets of theme
This method is the most common way of theme, storing multiple sets of themes locally, both with different namespaces, such as writing two sets of themes, a set called `day-theme`, a set called `night-theme`, and `night-theme.` Themes are all under a `.night-theme` namespace, and we dynamically add `.night-theme` on body; remove `.night-theme`.
#### How to use
> We have made corresponding changes here based on the official theme generation library [element-theme](https://github.com/ElementUI/element-theme).
First download [custom-element-theme](https://github.com/adempiere/custom-element-theme)
```bash
git@github.com:PanJiaChen/custom-element-theme.git
```
Globally installed theme generation tool
```bash
npm i element-theme -g
```
Enter the project directory Install dependencies
```bash
npm install
```
First execute `et -i` to generate `element-variables.scss` file that stores style variables, then enter `element-variables.scss` file to modify your own variables, execute `et` after modification, compile subject, and finally Execute `gulp` to generate a namespace. All generated files are in the `dist` directory. You just copy all the contents of the file to `src/assets/custom-theme` in the `adempiere-vue` project.
::: tip
If you need to modify the name of the package generation style namespace, just modify the [variable](https://github.com/adempiere/custom-element-theme/blob/master/gulpfile.js#L6).
:::
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/0726b472-90f4-4fe9-a665-26fb8f9795c3.gif)

View File

@ -0,0 +1,11 @@
## Guía de Webpack
Recomendado [survivejs](https://survivejs.com/webpack/foreword/)
Aquí para compartir la trilogía del paquete web, básicamente lee el ingeniero de configuración de webpack en este campo.
**No hay traducción al Español**
- [Comenzando con webpack 4 y aplicaciones de página única](https://github.com/wallstreetcn/webpack-and-spa-guide)
- [Las manos tocan tu mano, te usan con una postura razonable usando webpack4 (arriba)](https://juejin.im/post/5b56909a518825195f499806)
- [Las manos tocan tu mano, te usan con una postura razonable usando webpack4 (abajo)](https://juejin.im/post/5b5d6d6f6fb9a04fea58aabc)

View File

@ -0,0 +1,220 @@
# Compilar y Desplegar
## Compilar
Cuando se completan los proyectos, puedes compilar tu aplicación con solo ejecutar un comando:
```bash
# compilar para el entorno de producción
npm run build:prod
# compilar para el entorno de pruebas
npm run build:stage
```
Después de que el paquete de compilación sea exitoso, la carpeta `dist` se generará en el directorio raíz, que es la construcción de un archivo empaquetado, generalmente archivos estáticos como `***. js`, `***. css`, `index.html`, etc.
Si necesitas una compilación personalizada, como especificar el directorio dist, debes configurarlo a través de `outputDir` en [config](https://github.com/adempiere/adempiere-vue/blob/master/vue.config.js).
### Variables de entorno
La configuración de todos los entornos de prueba o variables de entorno formales se encuentra en el archivo `.env.xxxx` como [.env.development](https://github.com/adempiere/adempiere-vue/blob/master/.env.development).
Todos se inyectan en el contexto global a través de los complementos `webpack.DefinePlugin`.
::: tip ¡NOTA!
Las variables de entorno deben comenzar con `VUE_APP_`. Tales como: `VUE_APP_API`, `VUE_APP_TITLE`
Puedes acceder a ellas en el código de tu aplicación:
```js
console.log(process.env.VUE_APP_xxxx)
```
### Analizar el tamaño del archivo de compilación
Si tu archivo de compilación es grande, puedes optimizar tu código compilando y analizando la distribución del tamaño de los módulos dependientes utilizando `webpack-bundle-analyzer`.
```bash
npm run preview -- --report
```
Después de ejecutar, puedes ver la distribución de tamaño específico en [http://localhost:9526/report.html](http://localhost:9526/report.html)
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/3fddf034-2b38-4299-b0d2-b748fb2abef0.jpg)
::: tip
Se recomienda utilizar gzip, después de usarlo, el volumen será solo el 1/3 del original más o menos. También puedes usar Lazy Loading o Code Splitting.
:::
## Publicar
Para la publicación, solo tienes que publicar el archivo estático resultante después de la compilación, que generalmente es el archivo estático en la carpeta `dist`, en tu cdn o servidor estático. Ten en cuenta que `index.html` generalmente será una página de entrada para tu servicio de back-end. Es posible que debas cambiar la ruta de importación de la página después de determinar la estática para JS y CSS.
::: tip
En el despliegue puedes encontrar que la ruta del recurso es incorrecta, simplemente modifica la ruta del archivo de recurso `@/config/index.js`.
:::
```js
// los cambios se configuran según tu propia ruta
publicPath: './'
```
### Enrutador y servidor
En adempiere-vue, el enrutamiento front-end usa `vue-router`, por lo que tienes dos opciones: `browserHistory` y `hashHistory`.
Simplemente hablando, la diferencia entre ellos es el trato con el enrutamiento. `hashHistory` es procesado por la ruta que sigue de `#`, la gestión de enrutamiento de front-end a través de [HTML 5 History](https://developer.mozilla.org/en-US/docs/Web/API/History_API), y `browserHistory` es similar a nuestra ruta de acceso de página habitual, y no con `#`, pero debe a través de la configuración del servidor.
Este proyecto utiliza `hashHistory` de forma predeterminada, por lo que, si tienes `#` en tu URL y deseas deshacerte de él, debes cambiar a `browserHistory`.
Modificar el modo en `src/router/index.js`
```js
export default new Router({
// mode: 'history' // Necesita soporte de backend
})
```
::: tip
Ver detalles [vue-router document](https://router.vuejs.org/guide/essentials/history-mode.html)
:::
## Desplegar todo el Ecosistema
### Para todos los entornos, debe ejecutar las siguientes imágenes:
- [ADempiere gRPC](https://hub.docker.com/r/erpya/adempiere-grpc-all-in-one)
```shell
docker pull erpya/adempiere-grpc-all-in-one
```
- [Proxy ADempiere API](https://hub.docker.com/r/erpya/proxy-adempiere-api)
```shell
docker pull erpya/proxy-adempiere-api
```
- [ADempiere Vue](https://hub.docker.com/r/erpya/adempiere-vue)
```shell
docker pull erpya/adempiere-vue
```
- [ADempiere eCommerce](https://hub.docker.com/r/erpya/adempiere-ecommerce)
```shell
docker pull erpya/adempiere-ecommerce
```
### Ejecutar Docker Stack
```yaml
# docker-compose.yaml
version: '3.7'
services:
grpc-backend:
image: erpya/adempiere-grpc-all-in-one
container_name: adempiere-backend
stdin_open: true
tty: true
environment:
- SERVER_PORT=50059
- SERVICES_ENABLED=access; business; core; dashboarding; dictionary; enrollment; log; ui; workflow; store; pos; updater;
- SERVER_LOG_LEVEL=WARNING
- DB_HOST=postgres_host
- DB_PORT=5432
- DB_NAME=adempiere
- DB_USER=adempiere
- DB_PASSWORD=adempiere
- DB_TYPE=PostgreSQL
ports:
- 50059:50059
redis:
image: redis:4-alpine
container_name: adempiere-redis
stdin_open: true
tty: true
ports:
- '6379:6379'
es7:
image: docker.elastic.co/elasticsearch/elasticsearch:7.3.2
container_name: adempiere-eslastic-search
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- ./elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml:ro
ports:
- '9200:9200'
- '9300:9300'
environment:
- discovery.type=single-node
- cluster.name=docker-cluster
- bootstrap.memory_lock=true
- ES_JAVA_OPTS=-Xmx512m -Xms512m
api-rest:
image: erpya/proxy-adempiere-api
container_name: adempiere-proxy
depends_on:
- es7
- redis
stdin_open: true
tty: true
environment:
- SERVER_PORT=8085
- AD_DEFAULT_HOST=adempiere-backend
- AD_DEFAULT_PORT=50059
- ES_HOST=adempiere-eslastic-search
- ES_PORT=9200
- VS_ENV=dev
- INDEX=vue_storefront_catalog
- RESTORE_DB=N
ports:
- 8085:8085
vue-app:
image: erpya/adempiere-vue
container_name: adempiere-frontend
stdin_open: true
tty: true
environment:
- API_URL=http://adempiere-proxy:8085
ports:
- 9526:80
e-commerce:
image: erpya/adempiere-ecommerce
container_name: adempiere-ecommerce
stdin_open: true
tty: true
environment:
- SERVER_PORT=3000
- API_URL=http://adempiere-proxy:8085
- STORE_INDEX=vue_storefront_catalog
- VS_ENV=dev
ports:
- 3000:3000
```
Nota: El contenedor Elasticsearch requiere un archivo de configuración `elasticsearch.yaml`.
```shell
# requiere permisos de super usuario del sistema operativo ('su' o 'sudo')
docker-compose up
```
Contenedores de salida:
- adempiere-backend
- adempiere-redis
- adempiere-eslastic-search
- adempiere-proxy
- adempiere-frontend
- adempiere-ecommerce

View File

@ -0,0 +1,41 @@
# Variables de Entorno
`adempiere-vue` 4.0+ está construido en `vue-cli`, por lo que todas las variables de entorno se controlan en función de `vue-cli`.
[Documento oficial](https://cli.vuejs.org/guide/mode-and-env.html)
```
.env # cargado en todos los casos
.env.[mode] # solo cargado en el modo especificado
```
Un archivo env simplemente contiene pares de variables de entorno key=value:
```
FOO=bar
VUE_APP_SECRET=secret
```
::: tip ¡NOTA!
Las variables de entorno deben comenzar con `VUE_APP_`. Tales como: `VUE_APP_API`, `VUE_APP_TITLE`
Puedes acceder a ellas en el código de tu aplicación:
```js
console.log(process.env.VUE_APP_xxxx)
```
:::
Además de las variables `VUE_APP_*`, también hay dos variables especiales que siempre estarán disponibles en el código de tu aplicación:
- `NODE_ENV` - este será uno de "development, "production" o "test" dependiendo del modo en que se ejecute la aplicación.
- `BASE_URL` - esto corresponde a la opción `publicPath` en `vue.config.js` y es la ruta base en la que se implementa tu aplicación.
## Relacionado con la compilación
Además de algunas variables de entorno escritas en `.env`, hay algunas variables relacionadas con la compilación y la implementación que deben configurarse en `vue.config.js`.
Puedes establecer los diferentes parámetros ejecutando el entorno de juicio con `process.env.NODE_ENV`.
El código específico lo puedes aprender de [vue.config.js](https://github.com/adempiere/adempiere-vue/blob/master/vue.config.js)

View File

@ -0,0 +1,63 @@
# Importar Módulos de Terceros
Además de los componentes de element-ui y los componentes comerciales integrados en el andamio, a veces también necesitamos importar otros componentes externos.
Aquí para importar [vue-count-to](https://github.com/adempiere/vue-countTo) como ejemplo para introducir.
## Instalar dependencia
Ingresa el siguiente comando en la terminal para completar la instalación:
```bash
$ npm install vue-count-to --save
```
> al añadir `--save` se agregarán automáticamente las dependencias a package.json.
<br/>
## Uso
### Registro global
**main.js**
```js
import countTo from 'vue-count-to'
Vue.component('countTo', countTo)
```
```html
<template>
<countTo :startVal='startVal' :endVal='endVal' :duration='3000'></countTo>
</template>
```
### Registro local
```html
<template>
<countTo :startVal='startVal' :endVal='endVal' :duration='3000'></countTo>
</template>
<script>
import countTo from 'vue-count-to';
export default {
components: { countTo },
data () {
return {
startVal: 0,
endVal: 2017
}
}
}
</script>
```
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/8b95fac0-6691-4ad6-ba6c-e5d84527da06.gif)
<br/>
## Utiliza cualquier biblioteca de Javascript con Vue.js
[Utiliza cualquier biblioteca de Javascript con Vue.js](https://vuejsdevelopers.com/2017/04/22/vue-js-libraries-plugins/)

View File

@ -0,0 +1,116 @@
# Diseño
El diseño general de la página es la estructura de un producto y a menudo incluye navegación, barras laterales, breadcrumbs y contenido. Para comprender un proyecto de administración, primero comprende su diseño básico.
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/7066d74f-12c5-47d6-b6ad-f22b43fec917.png)
::: tip Código
[@/layout](https://github.com/adempiere/adempiere-vue/tree/master/src/layout)
:::
`@` es el [alias](https://webpack.js.org/configuration/resolve/#resolve-alias) de webpack. Si no lo entiendes por favor estudíalo por tu cuenta.
<br>
La mayoría de las páginas en `adempiere-vue` se basan en este `layout`, excepto las páginas individuales como: `login`, `404`, `401`, etc., las cuales no utilizan este diseño. También es fácil si se quiere tener varios diseños en un proyecto, simplemente hay que elegir un componente de diseño diferente en el enrutamiento de primer nivel.
```js
// Sin diseño
{
path: '/401',
component: () => import('errorPage/401')
}
// Con diseño
{
path: '/documentation',
// Puedes elegir diferentes componentes de diseño
component: Layout,
// Aquí la ruta se muestra en app-main
children: [{
path: 'index',
component: () => import('documentation/index'),
name: 'documentation'
}]
}
```
Esto utiliza vue-router [anidación de enrutamiento](https://router.vuejs.org/guide/essentials/nested-routes.html), por lo que, en general, agregar o modificar una página solo afectará el cuerpo principal de app-main. Otro contenido en el diseño, como: la barra lateral o la barra de navegación no cambiará con tu página principal.
```
/foo /bar
+------------------+ +-----------------+
| layout | | layout |
| +--------------+ | | +-------------+ |
| | foo.vue | | +------------> | | bar.vue | |
| | | | | | | |
| +--------------+ | | +-------------+ |
+------------------+ +-----------------+
```
<br>
## app-main
::: tip Código
[@/layout/components/AppMain](https://github.com/adempiere/adempiere-vue/blob/master/src/layout/components/AppMain.vue)
:::
Aquí hay una capa de `keep-alive`, afuera `app-main` es principalmente para almacenar en caché `<router-view>`, con la navegación de la pestaña `tabs-view` de la página, si no lo necesitas [eliminalo](tags-view.md).
`transition` define la animación de cambio entre páginas, puedes modificar la animación de transición según tus propias necesidades. [Documentación](https://vuejs.org/v2/guide/transitions.html) relacionada.
Se proporcionan dos animaciones de transición de forma predeterminada `fade` y `fade-transform`. Para la implementación específica de CSS, consulta [transition.scss](https://github.com/adempiere/adempiere-vue/blob/master/src/styles/transition.scss). Si necesitas cambiarla, puedes modificar `name` de `transition` en [AppMain.vue](https://github.com/adempiere/adempiere-vue/blob/master/src/layout/components/AppMain.vue).
<br>
## router-view
**Diferente enrutador, el mismo componente vue** En un trabajo real, hay muchas situaciones como:
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/ac5047c9-cb75-4415-89e3-9386c42f3ef9.jpeg)
El mismo componente se utiliza para crear y editar páginas. Por defecto, cuando se cambien estas dos páginas, no se activarán los hooks creados o montados de vue. [Oficialmente dice](https://router.vuejs.org/guide/advanced/data-fetching.html#data-fetching) que puedes hacer esto a través del cambio de reloj `$route`. A decir verdad, sigue siendo muy problemático. Más tarde descubrí que simplemente podía agregar una clave única a router-view para asegurar que los hooks de enrutamiento se vuelvan a representar cuando se cambia la ruta. Es mucho más simple.
```js
<router-view :key="key"></router-view>
computed: {
key() {
// solo asegúrate de que la clave (key) sea única
return this.$route.fullPath
}
}
```
::: tip
**O** Puedes declarar dos vistas diferentes, como `editForm` y `createForm` en este proyecto, pero introducirlo en el mismo componente.
Código: [@/views/example](https://github.com/adempiere/adempiere-vue/tree/master/src/views/example)
:::
```html
<!-- create.vue -->
<template>
<article-detail :is-edit='false'></article-detail> //crear
</template>
<script>
import ArticleDetail from './components/ArticleDetail'
</script>
<!-- edit.vue -->
<template>
<article-detail :is-edit='true'></article-detail> //editar
</template>
<script>
import ArticleDetail from './components/ArticleDetail'
</script>
```
## Móvil
La posición oficial de `element-ui` es de framework del lado del escritorio, y la mayoría de los proyectos de administración son complejos, es imposible cumplir con las interacciones del lado del escritorio y del lado del móvil a través de una simple adaptación. Por lo tanto, la interacción entre los dos extremos debe ser diferente. Se recomienda volver a hacer un sistema para dispositivos móviles.
En pocas palabras, este proyecto no se adaptará a móvil. Es una respuesta simple y puedes modificarlo tu mismo.

View File

@ -0,0 +1,202 @@
# Datos simulados
Los datos simulados son una parte integral del desarrollo front-end, el enlace clave para separar el desarrollo front-end y el back-end. Como acordamos anteriormente, la interfaz del lado del servidor, los datos de solicitud analógica e incluso la lógica pueden hacer que el desarrollo front-end sea independiente, no será bloqueado por el desarrollo del servidor.
## Swagger
En el proyecto de mi empresa, el backend simula los datos mediante [swagger](https://swagger.io/).
**swagger** es una herramienta de generación de documentos API REST que genera automáticamente documentación a partir de comentarios en el código. Puede ser multiplataforma, de código abierto, admite la mayoría de los idiomas, la comunidad es buena, en resumen, muy buena y recomendable.
[Demo en línea](http://petstore.swagger.io/?_ga=2.222649619.983598878.1509960455-2044209180.1509960455#/pet/addPet)
## Easy-mock
[vue-admin-template](https://github.com/adempiere/vue-admin-template) anteriormente utilizó [easy-mock](https://easy-mock.com/login) para simular datos.
Es una visualización pura de front-end y puede generar rápidamente servicios de persistencia para datos analógicos. Muy fácil de usar y también se puede combinar con `swagger`, tiene soporte para cross-domain, ya sea un equipo o un proyecto personal, vale la pena intentarlo.
[Demo en línea](https://easy-mock.com/)
::: warning
La versión en línea de `vue-admin-template` ya no usa `easy-mock`. Debido a que el servicio gratuito en línea es muy inestable, se colgará de vez en cuando. Si lo necesitas, puede crear tu propio servicio de acuerdo con su tutorial.
:::
## Mockjs
Como [adempiere-vue](https://github.com/adempiere/adempiere-vue) es un proyecto personal de front-end puro, todos los datos de simulación son generados por [mockjs](https://github.com/Nuysoft/Mock). Su principio es: interceptar todas las solicitudes y proxy al local, y luego simular datos, por lo que descubrirás que no se emiten solicitudes en `red`.
Pero su mayor problema es el mecanismo de implementación. Sobrescribe el objeto `XMLHttpRequest` del navegador para interceptar todas las solicitudes y el proxy al local. En la mayoría de los casos es bastante conveniente de usar, pero debido a que reescribe el objeto `XMLHttpRequest`, por ejemplo, el método `progress` o algunas bibliotecas de terceros que dependen de `XMLHttpRequest` serán incompatibles. Mirando los [issues](https://github.com/adempiere/adempiere-vue/issues?utf8=%E2%9C%93&q=mock) de mi proyecto, sabrás cuántas personas tienen problemas.
También tiene un problema porque son datos que se simulan localmente y en realidad no realizan ninguna solicitud de red. Por lo tanto, la depuración local es muy problemática y solo se puede depurar mediante `console.log`. Toma el ejemplo de `adempiere-vue`. Si deseas averiguar qué datos devuelve la api `getInfo()`, solo puedes saberlo mirando el código fuente o manualmente `Debug`.
## Nueva manera <Badge text="v4.0.0+"/>
Después de la versión `v4.0`, se lanzará un `mock-server` localmente para simular los datos, y el entorno en línea continuará utilizando `mockjs` para la simulación. (Debido a que este proyecto es un proyecto front-end puro, también puedes construir un servidor en línea para proporcionar datos).
La ventaja de esta manera es resolver los puntos críticos anteriores mientras se conservan las ventajas de 'mockjs'. Dado que nuestro simulacro se implementa completamente basado en `webpack-dev-serve`, `mock-server` se iniciará automáticamente junto con el proyecto, y también pasará [chokidar](https://github.com/paulmillr/chokidar) para observar los cambios en el contenido de la carpeta `mock`. Cuando se produce un cambio, la interfaz `mock-api` registrada previamente se borra y la nueva interfaz se vuelve a montar dinámicamente para admitir actualizaciones. Si estás interesado, puedes mirar el código [mock-server.js](https://github.com/adempiere/adempiere-vue/blob/master/mock/mock-server.js). Dado que es un verdadero "servidor", puedes conocer claramente la estructura de datos devuelta por la interfaz a través de la sección "red" de Chrome. Al mismo tiempo, resuelves el problema de que los `mockjs` anteriores rescriben el objeto`XMLHttpRequest`, lo que hace que muchas bibliotecas de terceros fallen.
Todas las solicitudes para este proyecto se envían a través del paquete [request.js](https://github.com/adempiere/adempiere-vue/blob/master/src/utils/request.js), mediante la lectura del código fuente puedes encontrar que todas las solicitudes están configuradas en `baseURL`, y este se configura dinámicamente al leer la variable de entorno `process.env.VUE_APP_BASE_API`, para que podamos usar diferentes entornos.
## Eliminar
Si no deseas usar `mock-server`, solo el middleware `after` de `webpack-dev-server` desde [vue.config.js](https://github.com/adempiere/adempiere-vue/blob/master/vue.config.js).
Por defecto, las solicitudes locales son proxy para `http://localhost:${port}/mock`, y puedes modificar 'proxy' si deseas ajustar a tu propia dirección simulada.
```js
proxy: {
// cambiar xxx-api/login => mock/login
// detalles: https://cli.vuejs.org/config/#devserver-proxy
[process.env.VUE_APP_BASE_API]: {
target: `http://localhost:${port}/mock`,
changeOrigin: true,
pathRewrite: {
['^' + process.env.VUE_APP_BASE_API]: ''
}
}
},
after: require('./mock/mock-server.js')
```
**Tenga en cuenta: esta operación requiere un reinicio del servidor.**
## Agregar
Si deseas agregar datos simulados, solo busca el archivo `mock` en la carpeta raíz, agrega la ruta correspondiente, intercepta y simula los datos.
Por ejemplo, necesito agregar una API para obtener la cantidad de comentarios debajo de un artículo en [src/api/article](https://github.com/adempiere/adempiere-vue/blob/master/src/api/article.js) a través de `fetchComments`. Primero crea una nueva api:
```js
export function fetchComments(id) {
return request({
url: `/article/${id}/comments`,
method: 'get'
})
}
```
Después de declarar la API, necesitamos encontrar la carpeta simulada correspondiente [mock/article.js](https://github.com/adempiere/adempiere-vue/blob/master/mock/article.js), debajo Creamos una API simulada que intercepte las rutas.
**Ten en cuenta que la intercepción simulada se basa en el enrutamiento. Asegúrate de que la ruta de datos simulados coincida con tu ruta de la API (soporte regular)**
```js
// fetchComments mock
{
// url debe coincidir con la ruta de tu api
// Por ejemplo, la ruta de fetchComments puede ser /article/1/comments or /article/2/comments
// Por lo que necesitas que coincida con regularidad
url: '/article/[A-Za-z0-9]/comments',
type: 'get', // Debe ser del mismo tipo que tu interfaz define
response: (req, res) => {
// return result
// req y res ver detalles
// https://expressjs.com/zh-cn/api.html#req
return {
code: 20000,
data: {
status: 'success'
}
}
}
}
```
## Cambiar
La operación más común es: has simulado algunos datos localmente, y una vez que el backend completa la API, reemplaza gradualmente la API del simulacro original.
Tomemos como ejemplo la API `getRoles` en [src/api/role.js](https://github.com/adempiere/adempiere-vue/blob/master/src/api/role.js). Originalmente se simuló de [mock/role/index.js](https://github.com/adempiere/adempiere-vue/blob/master/mock/role/index.js). Ahora necesitamos cambiarlo a datos reales de back-end, siempre que esté en [mock/role/index.js](https://github.com/adempiere/adempiere-vue/blob/master/mock/role/index.js). Encuentra la ruta correspondiente y luego elimínala. En este momento puedes ver los datos reales en `network`.
```js
// Lo declarado en la api
export function getRoles() {
return request({
url: '/roles',
method: 'get'
})
}
// Encuentra la ruta correspondiente y elimina
{
url: '/roles',
type: 'get',
response: _ => {
return {
code: 20000,
data: roles
}
}
},
```
## Servidores múltiples
Actualmente, el proyecto solo inicia un `mock-server`, por supuesto, también puedes tener tu propia interfaz `mock-server` o proxy. Algunas API pueden soportar este servicio, otras pueden soportar otros. Simplemente configúralos en un `baseURL` diferente. [@/utils/request.js](https://github.com/adempiere/adempiere-vue/blob/master/src/utils/request.js)
A continuación, configura múltiples `proxy` de acuerdo con las reglas de url establecidas en [vue.config.js](https://github.com/adempiere/adempiere-vue/blob/master/vue.config.js).
[Documentos relacionados](https://webpack.docschina.org/configuration/dev-server/#devserver-proxy)
## Habilitar simulación de front end puro
Ahora en [mock/index.js](https://github.com/adempiere/adempiere-vue/blob/master/mock/index.js#L19) también se encapsula un método simulado de front-end puro, solo necesita estar en [src/main.js](https://github.com/adempiere/adempiere-vue/tree/master/src):
```js
import { mockXHR } from '../mock'
mockXHR()
```
Esto se convertirá en pura información simulada de front-end y al igual que la versión anterior a la `v4.0`, el principio es el anterior. La [demo](https://adempiere.github.io/adempiere-vue) en línea que estás viendo actualmente es así.
## Cambiar datos simulados locales y en línea
En muchas ocasiones, encontramos un uso local de datos simulados, entornos en línea que utilizan datos reales o entornos diferentes que utilizan datos diferentes.
- **Easy-Mock**
Debes asegurarte de que tu API simulada local sea coherente con todas las demás direcciones, excepto la ruta raíz. como:
```
https://api-dev/login // Solicitud local
https://api-prod/login // Solicitud en línea
```
Podemos usar las [variables de entorno](/guide/essentials/deploy.html#environmental-variables) para hacer diferentes entornos y solicitar diferentes rutas base de la API.
```bash
# .env.development
VUE_APP_BASE_API = '/dev-api' #Inyecta la ruta raíz de la API local
```
```bash
# .env.production
VUE_APP_BASE_API = '/prod-api' #Inyecta la ruta raíz de la API de producción
```
Luego crea una instancia de `axios` basada en la variable de entorno para tener una `baseURL` diferente.
[@/utils/request.js](https://github.com/adempiere/adempiere-vue/blob/master/src/utils/request.js)
```js
// crear una instancia de axios
const service = axios.create({
baseURL: process.env.BASE_API, // base_url de la API
timeout: 5000 // tiempo de espera de la solicitud
})
```
De esta manera, podemos cambiar automáticamente las APIs locales y en línea en función de las variables de entorno.
- **Mock.js**
Cuando usamos `Mock.js` para simular datos localmente, el método de la API del mundo real se usa en línea. Esto es similar al método de easy-mock anterior. Principalmente damos por hecho que cuando se trata de un entorno en línea, usamos la API del mundo real. Solo importamos `Mock.js` localmente.
```js
// main.js
// se requiere usar variables de entorno para determinarlo
if (process.env.NODE_ENV === 'development') {
require('./mock') // datos de simulación
}
```
Los datos simulados solo se importan en el entorno local.

View File

@ -0,0 +1,143 @@
# Nueva Página
Si estás familiarizado con `vue-router`, entonces será muy simple.
Primero agrega la ruta a `@/router/index.js`.
**Por ejemplo: agregar una página de Excel**
```js
{
path: '/excel',
component: Layout,
redirect: '/excel/export-excel',
name: 'excel',
meta: {
title: 'excel',
icon: 'excel'
}
}
```
::: tip
Simplemente crea una ruta en blanco basada en 'layout', y agrega una ruta debajo de 'children'.
:::
```js
{
path: '/excel',
component: Layout,
redirect: '/excel/export-excel',
name: 'excel',
meta: {
title: 'excel',
icon: 'excel'
},
children: [
{
path: 'export-excel',
component: ()=>import('excel/exportExcel'),
name: 'exportExcel',
meta: { title: 'exportExcel' }
}
]
}
```
**En esta barra lateral aparecerá el menu-item**
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/2ab6921d-f9bb-4fbb-a151-0e6027e23a6e.png)
<br/>
:::tip
Cuando en `children` se declare solo una ruta, no habrá flecha de expansión. Si el número de rutas en `children` es mayor que 1, habrá una flecha de expansión, como se muestra a continuación.
Si deseas ignorar esta decisión automática, puedes usar `alwaysShow: true`, para que ignore la regla previamente definida y muestre la ruta raíz. Consulta [Enrutador y Navegación](router-and-nav.md) para más detalles..
:::
```js
{
path: '/excel',
component: Layout,
redirect: '/excel/export-excel',
name: 'excel',
meta: {
title: 'excel',
icon: 'excel'
},
children: [
{ path: 'export-excel', component:()=>import('excel/exportExcel'), name: 'exportExcel', meta: { title: 'exportExcel' }},
{ path: 'export-selected-excel', component:()=>import('excel/selectExcel'), name: 'selectExcel', meta: { title: 'selectExcel' }},
{ path: 'upload-excel', component:()=>import('excel/uploadExcel'), name: 'uploadExcel', meta: { title: 'uploadExcel' }}
]
}
```
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/89d6a0b8-5cf7-4a19-9afd-7267ec454066.png)
**En la barra lateral aparecerá el `submenu`.**
<br/>
## Rutas anidadas
Si tienes una Ruta anidada, como [@/views/nested](https://github.com/adempiere/adempiere-vue/tree/master/src/views/nested),
**no olvides agregar manualmente `< router-view >` al archivo raíz del directorio secundario**.
```html
<!-- parent view -->
<template>
<div>
<!-- xxx html dom -->
<router-view />
</div>
</template>
```
Por ejemplo: [@/views/nested/menu1/index.vue](https://github.com/adempiere/adempiere-vue/blob/master/src/views/nested/menu1/index.vue).
**NOTA:** Agrega tantos `<ruter-view>` como nivel de rutas anidadas.
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/9459de62-64d0-4819-9730-daf3f9889018.png)
<br/>
## Crear vista
Después de agregar la ruta, crea una vista debajo de `@/views`. Como de costumbre, un enrutador corresponde a una vista.
Sugerencia: si un componente o una función de utilidades solo se usa en esta vista, simplemente crea una carpeta de componentes en esta vista, es más conveniente para cada módulo mantener sus propios `utils` o `components`.
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/8ca55a30-c22c-4143-aa8d-2a0d3e04fc33.png)
<br/>
## Crear API
Finalmente, bajo la carpeta [@/api](https://github.com/adempiere/adempiere-vue/tree/master/src/api) crea el servicio api correspondiente para este módulo.
## Crear componente
Escribe personalmente hábitos de proyecto vue, el `@/components` global solo escribirá algunos componentes globales, como texto enriquecido, varios componentes de búsqueda, componentes de fecha empaquetada, etc., pueden ser componentes compartidos. Cada página o componente comercial específico del módulo se escribe bajo las vistas actuales. Por ejemplo: `@/views/article/components/xxx.vue`. Esta división reduce en gran medida los costos de mantenimiento.
** ¡Recuerda que el mayor beneficio de dividir componentes no es el código compartido sino la mantenibilidad! **
## Crear estilo
El estilo y los componentes de la página son los mismos. `@/style` global escribe un estilo común global. El estilo de cada página está escrito bajo el `views` actual. Recuerda agregar `scoped` o espacio de nombres para evitar Causas de contaminación de estilo global.
```css
<style>
/* estilos globales */
</style>
<style scoped>
/* estilos locales */
.xxx-container{
/* name scoped */
xxx
}
</style>
```

View File

@ -0,0 +1,79 @@
# Permisos
Se ha introducido en detalle en este artículo--[手摸手,带你用 vue 撸后台 系列二(登录权限篇)](https://juejin.im/post/591aa14f570c35006961acac).
La implementación de permisos de este proyecto es: comparar la tabla de enrutamiento obteniendo con el permiso del usuario actual, generar la tabla de enrutamiento accesible por el usuario, y montarla dinámicamente en `router` a través de `router.addRoutes`.
Pero, en realidad, la lógica empresarial de muchas organizaciones puede no ser el caso. Por ejemplo, el requisito de muchas empresas es que los permisos de cada página se configuren dinámicamente, a diferencia de la configuración predeterminada en este proyecto. Pero, de hecho, el principio es el mismo. Por ejemplo, puedes configurar dinámicamente los permisos para cada página a través de un control de árbol u otra representación, y luego almacenar esta tabla de enrutamiento en el back-end. Cuando el usuario inicia sesión para obtener 'roles', el front-end solicita la tabla de enrutamiento accesible al back-end de acuerdo con 'roles', de modo que las páginas accesibles se generan dinámicamente. Después de eso, router.addRoutes se monta dinámicamente en el enrutador. Encontrarás lo mismo, nunca cambian su caso.
Solo un paso más para mapear la tabla de enrutamiento de retorno del back-end con los componentes locales. [issue](https://github.com/adempiere/adempiere-vue/issues/293)
```js
const map={
login:require('login/index').default // síncrono
login:()=>import('login/index') // asíncrono
}
// El mapa en el que tienes un servidor es similar a
const serviceMap=[
{ path: '/login', component: 'login', hidden: true }
]
// Después de recorrer este mapa, genera dinámicamente asyncRoutes
// y reemplaza el componente con map[component]
```
Ps: No descartes que este proyecto aumente el panel de control para admitir permisos de configuración dinámica real.
## Modificación lógica
El código de control del nivel de enrutamiento ahora está en `@/permission.js`. Si deseas cambiar la lógica, puedes utilizar el hook `next()` directamente en la lógica de juicio apropiada.
## Directiva de permiso
Escribe una directiva de permisos, y podrás implementar fácil y rápidamente el juicio de permisos a nivel de botón. [v-permission](https://github.com/adempiere/adempiere-vue/tree/master/src/directive/permission)
**Uso**
```html
<template>
<!-- Admin puede ver esto -->
<el-tag v-permission="['admin']">admin</el-tag>
<!-- Editor puede ver esto -->
<el-tag v-permission="['editor']">editor</el-tag>
<!-- Editor puede ver esto -->
<el-tag v-permission="['admin','editor']">Tanto admin como editor pueden ver esto</el-tag>
</template>
<script>
// Por supuesto, también puedes registrarlo por conveniencia.
import permission from '@/directive/permission/index.js'
export default{
directives: { permission }
}
</script>
```
**Limitaciones**
En algunos casos, no es adecuado usar v-permission, como en el componente de element 'Tab', que solo se puede lograr configurando manualmente v-if.
Puedes usar la función de juicio de permiso global. El uso es similar a la instrucción `v-permission`.
```html
<template>
<el-tab-pane v-if="checkPermission(['admin'])" label="Admin">Admin puede ver esto</el-tab-pane>
<el-tab-pane v-if="checkPermission(['editor'])" label="Editor">Editor puede ver esto</el-tab-pane>
<el-tab-pane v-if="checkPermission(['admin','editor'])" label="Admin-OR-Editor">Tanto admin como editor pueden ver esto</el-tab-pane>
</template>
<script>
import checkPermission from '@/utils/permission'
export default{
methods: {
checkPermission
}
}
</script>
```

View File

@ -0,0 +1,310 @@
# Enrutador y Navegación
El enrutador y la navegación son el esqueleto clave para organizar un sistema de gestión.
El enrutador de este proyecto y la navegación están unidos, por lo que solo debes configurar la ruta en `@/router/index.js` y la navegación de la barra lateral se generará de manera dinámica automáticamente. Esto reduce en gran medida la carga de trabajo al no editar manualmente la barra lateral de navegación. Por supuesto, debes seguir muchos convenios al configurar la ruta.
## Configuración
En primer lugar, vamos a conocer la configuración que se le proporciona a una ruta.
```js
// si es true, no aparecerá en la barra lateral de navegación.
// p.ej. inicio de sesión, página 401 o como algunas de edición /edit/1 (Predeterminado: false)
hidden: true
// No se puede hacer clic en esta ruta en breadcrumb cuando se establece noRedirect
redirect: noRedirect
// cuando agregas más de un hijo a un elemento ruta, automáticamente se
// convierte en modo anidado, cuando solo hay un hijo se muestra como ruta
// raíz de forma predeterminada, pero si deseas mostrarla en modo anidado,
// aunque solo sea una puedes establecer:
// alwaysShow: true
// para que ignore las reglas definidas previamente y siempre se muestre
// en modo anidado
alwaysShow: true
// establece el nombre del enrutador. Debe configurarse para evitar problemas con <keep-alive>.
name: 'router-name'
meta: {
// roles requeridos para navegar a esta ruta. Admite múltiples permisos de apilamiento.
// si no se establece significa que no necesita ningún permiso.
roles: ['admin', 'editor']
// El título de la ruta para mostrar en varios componentes (por ejemplo, barra lateral, breadcrumbs).
title: 'title'
// clase de icono SVG
icon: 'svg-name' // or el-icon-x
// si es true, la ruta no será almacenada en caché por <keep-alive> (Predeterminado: false)
noCache: true
// si es false, el elemento estará oculto en el breadcrumb (Predeterminado: true)
breadcrumb: false
// if set to true, it can be fixed in tags-view (default false)
affix: true // this is very useful in some scenarios, // click on the article to enter the article details page,
// When you set, the related item in the sidebar will be highlighted
// for example: a list page route of an article is: /article/list
// at this time the route is /article/1, but you want to highlight the route of the article list in the sidebar,
// you can set the following
activeMenu: '/article/list'
}
```
<br/>
**Ejemplo**
```js
{
path: '/permission',
component: Layout,
redirect: '/permission/index',
hidden: true,
alwaysShow: true,
meta: { roles: ['admin','editor'] }, // puedes establecer roles en la navegación raíz
children: [{
path: 'index',
component: _import('permission/index'),
name: 'permission',
meta: {
title: 'permission',
icon: 'lock',
roles: ['admin','editor'], // o puedes establecer roles solamente en la subnavegación
noCache: true
}
}]
}
```
## Enrutador
Hay dos tipos de rutas aquí, `constantRoutes` y `asyncRoutes`.
**constantRoutes:** representa rutas que no requieren acceso dinámico, como la página de inicio de sesión, 404, página general, etc.
**asyncRoutes:** representa las páginas que requieren permisos de juicio dinámico y se agregan dinámicamente a través de `addRouters`. Los detalles se introducirán en [Permisos](permission.md).
::: tip
Todas las páginas de enrutamiento aquí usan la `carga diferida del enrutador`, como se describe en el [documento](/guide/advanced/lazy-loading.md).
Si deseas saber más sobre browserHistory y hashHistory, consulta [Compilar y Desplegar](deploy.md).
:::
Las otras configuraciones no son diferentes a las de [vue-router](https://router.vuejs.org/en/) oficial, así que consulta la documentación tú mismo.
::: warning ADVERTENCIA
Hay algo en lo que debes tener cuidado, la página 404 debe ser la última en cargarse, si se declara en constantRoutes. Las páginas declaradas posteriormente se bloquearán en 404, consulta los detalles del problema: [cuando addRoutes tiene una ruta comodín para 404 no funciona](https://github.com/vuejs/vue-router/issues/1176)
:::
## Barra lateral
La barra lateral del proyecto se basa principalmente en `el-menu` de element-ui.
Se introdujo en el front, la barra lateral se genera dinámicamente al leer la ruta y se combina con el juez de permisos, pero también debe soportar la anidación infinita de rutas, por lo que aquí también se usa para los componentes recursivos.
> Código: [@/layout/components/Sidebar](https://github.com/adempiere/adempiere-vue/tree/master/src/layout/components/Sidebar)
Es posible modificar el estilo predeterminado de la barra lateral de `element-ui`. Todo el CSS lo puedes encontrar en [@/styles/sidebar.scss](https://github.com/adempiere/adempiere-vue/blob/master/src/styles/sidebar.scss) y modificarlo para satisfacer tus necesidades.
**Aquí hay que prestar atención**. La barra lateral general tiene dos formas, `submenu` y `el-menu-item`. Uno es un submenú anidado, el otro es un enlace directo. Como se muestra abajo:
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/e94739d6-d701-45c8-8c6e-0f4bb10c3b46.png)
La barra lateral ya te ha ayudado a hacer un juicio. Cuando agregas más de un hijo a un elemento, automáticamente se convierte en modo anidado. Si la ruta hijo es exactamente igual a 1, esta se muestra como ruta raíz en la barra lateral de forma predeterminada. Si no quieres que esto suceda, puedes desactivar esta función configurando `alwaysShow: true` en la ruta raíz. Como:
```js
// sin submenu, porque children.length === 1
{
path: '/icon',
component: Layout,
children: [{
path: 'index',
component: ()=>import('svg-icons/index'),
name: 'icons',
meta: { title: 'icons', icon: 'icon'}
}]
},
// con submenu, porque children.length >= 1
{
path: '/components',
component: Layout,
name: 'component-demo',
meta: {
title: 'components',
icon: 'component'
},
children: [
{ path: 'tinymce', component: ()=>import('components-demo/tinymce'), name: 'tinymce-demo', meta: { title: 'tinymce' }},
{ path: 'markdown', component: ()=>import('components-demo/markdown'), name: 'markdown-demo', meta: { title: 'markdown' }},
]
}
```
::: tip unique-opened
Puedes configurar `unique-opened` en [Sidebar/index.vue](https://github.com/adempiere/adempiere-vue/blob/master/src/layout/components/Sidebar/index.vue). Para controlar la barra lateral, si deseas mantener solo un submenú expandido.
:::
## Rutas anidadas
Si tienes una ruta anidada, como [@/views/nested](https://github.com/adempiere/adempiere-vue/tree/master/src/views/nested),
**No olvides agregar manualmente `<router-view>` al archivo raíz del directorio secundario**.
```html
<!-- parent view -->
<template>
<div>
<!-- xxx html dom -->
<router-view />
</div>
</template>
```
Tal como: [@/views/nested/menu1/index.vue](https://github.com/adempiere/adempiere-vue/blob/master/src/views/nested/menu1/index.vue).
**NOTA:** Tantos `<router-view>` como nivel de rutas anidadas.
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/9459de62-64d0-4819-9730-daf3f9889018.png)
<br/>
## Clic en la barra lateral para actualizar la ruta actual
Antes de utilizar el modelo de desarrollo spa (aplicación de página única), cada vez que el usuario hace clic en la barra lateral solicitará nuevamente esta página, el usuario gradualmente desarrolló el hábito de hacer clic en la ruta actual en la barra lateral para actualizar la vista. Pero ahora en el spa no es lo mismo, el usuario hace clic en la ruta resaltada actualmente y no actualiza la vista, porque el vue-router interceptará tu ruta, determina que la url no cambia, por lo que no activará ningún hook o cambios en la vista. [Problema relacionado](https://github.com/vuejs/vue-router/issues/296), la comunidad también ha discutido sobre el tema.
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/5d0b0391-ea6a-45f2-943e-aff5dbe74d12.png)
`yyx990803` también dijo que quería agregar una forma de actualizar la vista, y luego cambió de opinión nuevamente /(ㄒ o ㄒ)/~~ Pero la cuestión es la siguiente, ¿qué debemos hacer? Dijo que no activaría nada sin cambiar la URL actual, así que ¿puedo forzar al activador? El truco es simple. Al cambiar la consulta de la URL para activar los cambios de vista: escuchamos el evento clic de cada enlace en la barra lateral, cada clic realizará una consulta diferente en el enrutador para garantizar que la vista se actualice.
```js
clickLink(path) {
this.$router.push({
path,
query: {
//Asegúrate de que, en cada clic, query no sea el mismo
//para garantizar que se actualice la vista
t: +new Date()
}
})
}
```
ps: No olvides agregar una `key` única a `router-view`, como `<router-view :key="$route.path"></router-view>`.
También hay un inconveniente con el feo sufijo `query` al final de la URL, como `xxx.com/article/list?t=1496832345025`
Puedes saber del problema anterior que hay muchas otras opciones. En el proyecto de mi empresa, la solución adoptada es determinar si la ruta del menú en la que se hace clic actualmente es coherente con la ruta actual. Sin embargo, cuando sea coherente, saltará a una página de redireccionamiento dedicada, que a su vez Redirigirá la ruta para Ir a la página, esto tendrá un efecto de actualización.
**Ejemplo**
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/0dd7f78b-0fb5-4c7d-8236-cee78f960984.jpg)
Haz clic en el botón de cambio de tamaño global que se muestra en la imagen y verás que la página `app-main` se ha actualizado. Se utiliza el método de redireccionamiento a la página `Redirect` y luego redirecciona de nuevo a la página original.
Redireccionar la página a `/redirect` al hacer clic
```js
const { fullPath } = this.$route
this.$router.replace({
path: '/redirect' + fullPath
})
```
La página `redirect` se redirige de nuevo a la página original
```js
// redirect.vue
// https://github.com/adempiere/adempiere-vue/blob/master/src/views/redirect/index.vue
export default {
beforeCreate() {
const { params, query } = this.$route
const { path } = params
this.$router.replace({ path: '/' + path, query })
},
render: function(h) {
return h() // evitar mensaje de advertencia
}
}
```
<br>
## Breadcrumb
Este proyecto también incluye una navegación con breadcrumb, que también se genera dinámicamente por el cambio de ruta al observar $route. Es lo mismo con el menú, también puedes configurarlo en el enrutamiento y agregar algunos atributos personalizados a las necesidades de tu negocio en route.meta attr. Por ejemplo, puedes declarar `breadcrumb:false` en la ruta para que no se muestre en la sección breadcrumb.
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/4c60b3fc-febd-4e22-9150-724dcbd25a8e.gif)
> Código correspondiente: [@/components/Breadcrumb](https://github.com/adempiere/adempiere-vue/blob/master/src/components/Breadcrumb/index.vue)
## Problema de desplazamiento de la barra lateral
Las versiones anteriores de scroll se manejaban con css
```css
overflow-y: scroll;
::-webkit-scrollbar {
display: none;
}
```
Pero usar algún hack de CSS tiene algunos problemas, en Firefox u otras versiones inferiores del navegador serán menos hermosas.
En segundo lugar, en el caso de colapsos de la barra lateral, limitados al `menu` de `element-ui`, no se pueden manejar de esta manera.
Entonces, la versión actual usa `el-scrollbar` para encargarse del problema de desplazamiento de la barra lateral.
::: tip Código
[@/layout/components/Sidebar](https://github.com/adempiere/adempiere-vue/blob/master/src/layout/components/Sidebar/index.vue)
:::
## Enlace externo en la barra lateral <Badge text="v3.8.2+"/>
También puedes configurar un enlace externo en la barra lateral. Siempre y cuando coloques una dirección URL correcta en `path`, podrás abrir esta página cuando hagas clic en la barra lateral.
P.ej.
```json
{
"path": "external-link",
"component": Layout,
"children": [
{
"path": "https://github.com/adempiere/adempiere-vue",
"meta": { "title": "externalLink", "icon": "link" }
}
]
}
```
## Sidebar expands by default
In some scenarios, users need to expand some of the `sub-menu` in the sidebar by default, as shown below:
<img :src="$withBase('/images/default-openeds.jpg')" alt="default-openeds.jpg" width="250px">
Can be set through `default-openeds`, first find [Sidebar Code](https://github.com/adempiere/adempiere-vue/blob/master/src/layout/components/Sidebar/index.vue)
```html
<el-menu
:default-openeds="['/example','/nested']" // Add this line of code
:default-active="activeMenu"
:collapse="isCollapse"
:background-color="variables.menuBg"
:text-color="variables.menuText"
:unique-opened="false"
:active-text-color="variables.menuActiveText"
:collapse-transition="false"
mode="vertical"
>
<sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path" />
</el-menu>
```
**Note: default-openeds = "['example', 'nested']" is filled with route-path of submenu**

View File

@ -0,0 +1,101 @@
# Trabajar con el Servidor
## Flujo de solicitudes de front-end
En `adempiere-vue`, una interfaz de usuario completa interactúa con el flujo de procesamiento del lado del servidor de la siguiente manera:
1. Interacción de componentes UI;
2. Llamar a la función de solicitud de servicio API de gestión unificada;
3. Enviar solicitudes utilizando request.js;
4. Obtener respuesta del servidor;
5. Actualizar datos;
Como puedes ver en el flujo anterior, para facilitar la administración y el mantenimiento, el procesamiento de solicitudes unificadas se coloca en la carpeta `src/api` y los archivos generalmente se dividen de acuerdo con la latitud del modelo, como:
```
api/
login.js
article.js
remoteSearch.js
...
```
## request.js
`@/utils/request.js` se basa en [axios](https://github.com/axios/axios), para facilitar el manejo uniforme de POST, GET y otros parámetros de solicitud, encabezados de solicitud y mensajes de error. Para ver mas específico vea [request.js](https://github.com/adempiere/adempiere-vue/blob/master/src/utils/request.js).
Encapsula el 'interceptor de solicitud' global, el 'interceptor de respuesta', el 'manejo unificado de errores', el 'tiempo de espera unificado, la configuración de baseURL, etc.'
## Un ejemplo de solicitud de una lista de artículos:
```js
// api/article.js
import request from '../utils/request';
export function fetchList(query) {
return request({
url: '/article/list',
method: 'get',
params: query
})
}
// views/example/list
import { fetchList } from '@/api/article'
export default {
data() {
list: null,
listLoading: true
},
methods: {
fetchData() {
this.listLoading = true
fetchList().then(response => {
this.list = response.data.items
this.listLoading = false
})
}
}
}
```
## Establecer múltiples baseURLs
Podemos solicitar múltiples direcciones api configurando múltiples `baseURL`s a través de [variables de entorno](/guide/essentials/deploy.html).
```bash
# .env.development
VUE_APP_BASE_API = '/dev-api' #Inyecta la ruta raíz de la api
VUE_APP_BASE_API2 = '/dev-api2' #Inyecta la ruta raíz de la api
```
Luego crea una instancia `axios` basada en la variable de entorno, dándole una `baseURL` [@/utils/request.js](https://github.com/adempiere/adempiere-vue/blob/master/src/utils/request.js) diferente.
```js
// crear una instancia de axios
const service = axios.create({
baseURL: process.env.BASE_API, // api base_url
timeout: 5000 // tiempo de espera agotado
})
const service2 = axios.create({
baseURL: process.env.BASE_API2, // api base_url
timeout: 5000 // tiempo de espera agotado
})
```
O
```js
export function fetchList(query) {
return request({
url: '/article/list',
method: 'get',
params: query,
baseURL: 'xxxx' // cobertura directa
})
}
```
## Cambiar de Mock directamente a la solicitud del servidor
Ver [Mock Data](mock-api.md)

146
docs/es/guide/essentials/style.md Executable file
View File

@ -0,0 +1,146 @@
# Estilo
## Módulos CSS
En el proceso de desarrollo del estilo, hay dos cuestiones más destacadas:
- Contaminación global —— El selector en el archivo CSS es global. Si el mismo nombre del selector esta en diferentes archivos, de acuerdo con el orden en el archivo de generación de compilación, los estilos generados más adelante sobrescribirán los anteriores.
- Selector complejo —— Para evitar los problemas anteriores, debemos tener cuidado al escribir estilos, el nombre de la clase estará marcado con una serie de restricciones, el desarrollo entre varias personas también es muy fácil de conducir al caos con los nombres de los estilos. Los nombres de las clases son cada vez más largos. Eventualmente, es difícil de mantener.
Afortunadamente, vue nos proporciona [scoped](https://vue-loader.vuejs.org/guide/scoped-css.html#mixing-local-and-global-styles) el cual puede resolver fácilmente el problema anterior. Como su nombre indica, agrega un concepto de alcance (scoped) al css.
```css
/* Antes de compilar */
.example {
color: red;
}
/* Despues de compilar */
.example[_v-f3f3eg9] {
color: red;
}
```
Si agregas `<style scoped>` el css solo tendrá efecto en el componente actual. Para obtener documentación detallada, consulta [vue-loader](https://vue-loader.vuejs.org/guide/scoped-css.html#mixing-local-and-global-styles)
::: tip
Con scoped, los estilos del componente principal no se filtrarán a los componentes secundarios. Sin embargo, el nodo raíz de un componente secundario se verá afectado tanto por el CSS con scoped del padre como por el CSS con scoped del hijo. Esto es así para que el padre pueda aplicar estilo al elemento raíz hijo con fines de diseño.
:::
<br/>
## Estructura del proyecto
Todos los estilos globales de adempiere-vue se configuran en el directorio `@/styles`.
```bash
├── styles
│ ├── btn.scss # botones css
│ ├── element-ui.scss # estilo global personalizado de element-ui
│ ├── index.scss # estilo global común
│ ├── mixin.scss # global sass mixin
│ ├── sidebar.scss # barra lateral css
│ ├── transition.scss # animación de transición de vue
│ └── variables.scss # variables globales
```
El flujo de trabajo común es que los estilos globales se escriban en el directorio `src/styles` y el estilo propio de cada página se escriba en su propio archivo `.vue`.
```css
<style>
/* estilos globales */
</style>
<style scoped>
/* estilos locales */
</style>
```
## Estilo de element-ui personalizado
Ahora hablemos sobre cómo sobrescribir el estilo de element-ui. Debido a que el estilo de element-ui esta importado de manera global, no puedes agregar `scoped` a una página para sobrescribirlo, en caso de que quieras sobrescribir el estilo de element solamente en esa página, puedes agregar una clase en su padre, utilizando espacio de nombres para resolver este problema.
```css
.article-page {
/* tu espacio de nombres */
.el-tag {
/* etiqueta del elemento de element-ui */
margin-right: 0px;
}
}
```
**Por supuesto, también puedes usar los selectores profundos como se describe a continuación.**
## Selectores profundos
**El componente principal cambia el estilo del componente secundario.**
Si quieres que un selector en estilos con scoped sea "profundo", es decir, que afecte a los componentes secundarios, puedes usar el combinador `>>>`:
```css
<style scoped>
.a >>> .b { /* ... */ }
</style>
```
Se compilará como:
```css
.a[data-v-f3f3eg9] .b {
/* ... */
}
```
Es posible que algunos preprocesadores, como SASS, no puedan analizar `>>>` correctamente. En esos casos, puedes usar el combinador /deep/ en su lugar, es un alias para `>>>` y funciona exactamente igual.
```css
.xxx-container >>> .el-button{
xxxx
}
```
[Documentación oficial](https://vue-loader.vuejs.org/en/features/scoped-css.html)
## Postcss
Hablemos de la configuración de postcss. Después de la nueva versión de la inicialización [plantilla vue-cli de webpack](https://github.com/vuejs-templates/webpack), hay un `postcss.config.js` predeterminado en el directorio raíz. Por defecto, `vue-loader` leerá la configuración de postcss, así que aquí puede cambiar la configuración directamente. La configuración es la misma que [postcss](https://github.com/postcss/postcss).
```javascript
// postcss.config.js
module.exports = {
plugins: {
autoprefixer: {}
}
}
// package.json
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
```
Como se describe en el código anterior, el corrector automático lee los parámetros de configuración de browserslist en package.json.
- `> 1%` Compatible con navegadores con uso global superior al 1%
- `last 2 versions` Compatible con las dos últimas versiones de cada navegador
- `not ie <= 8` No compatible con ie8 e inferiores
Más detalles [browserslist](https://github.com/ai/browserslist)
`postcss` tiene muchas otras características [para explorar por tu cuenta](https://www.postcss.parts/)
## Mixin
Este proyecto no está configurado para inyectar automáticamente sass mixin en el estilo global, por lo que debes insertarlo manualmente.
```scss
<style rel="stylesheet/scss" lang="scss">
@import "src/styles/mixin.scss";
</style>
```
Si necesitas inyectar automáticamente mixin global, puedes usar [sass-resources-loader](https://github.com/shakacode/sass-resources-loader).

View File

@ -0,0 +1,111 @@
# Etiquetas View
Esta característica es para responder a las necesidades de las personas. De hecho, no utilizo esta característica en proyectos de la empresa o proyectos personales. En el pasado, esos frameworks tradicionales de back-end a menudo incluían esta característica. Dado que la mayoría de los proyectos de back-end anteriores tenían varias páginas, la característica de navegación Etiquetas View todavía tiene un significado básico. La mayoría de ellos se basan en el iframe.
Sin embargo, con el paso del tiempo, los proyectos en back-end son casi todos spa (desarrollo de una aplicación web de una sola página), y obviamente no es apropiado usar la forma anterior para implementar la navegación con etiquetas.
Entonces el plan actual es:
Usar una combinación de `keep-alive` y `router-view`.
Código: `@/layout/components/AppMain.vue`
```html
<keep-alive :include="cachedViews">
<router-view></router-view>
</keep-alive>
```
La acción real de las etiquetas de navegación view es equivalente a otro modo de visualización de navegación. De hecho, es un router-link, y al hacer clic salta a la página correspondiente. Luego estamos escuchando los cambios en la ruta `$route` para determinar si la página actual necesita ser recargada o almacenada en caché.
## visitedViews && cachedViews
La etiqueta view actual mantiene dos matrices.
- visitedViews : La página que el usuario ha visitado es una colección de matrices de etiquetas que se muestran en la barra de navegación de etiquetas.
- cachedViews : La ruta actual keep-alive. Puedes establecer si deseas o no almacenar en caché la ruta, configurandola con `meta.noCache`. [Documento de configuración](router-and-nav.md)
## Precauciones
Debido a que keep-alive y router-view están fuertemente acoplados, y no es difícil encontrar que keep-alive incluye el nombre predeterminado del componente, es necesario mirar el documento y el código fuente al escribir el componente de enrutamiento correspondiente al enrutador y la ruta de enrutamiento.
Asegúrate de que el nombre de ambos sea exactamente el mismo. (Ten en cuenta que el nombre debe ser tan único como sea posible. Recuerda no duplicar el nombre de algunos componentes o hacer referencia al último problema de desbordamiento de memoria de forma recursiva).
**DEMO:**
```js
//Definir rutas
{
path: 'create-form',
component: ()=>import('@/views/form/create'),
name: 'createForm',
meta: { title: 'createForm', icon: 'table' }
}
```
```js
//La vista correspondiente de la ruta. tales como: form/create
export default {
name: 'createForm'
}
```
Asegúrate de que los dos nombres sean iguales. Recuerda no escribir duplicados o errores. De forma predeterminada, si no escribes el nombre, no se almacenará en caché.
Para más detalles, ver [issue](https://github.com/vuejs/vue/issues/6938#issuecomment-345728620).
## Cuando la caché no es adecuada para la situación
Las soluciones actualmente en caché no son adecuadas para ciertos servicios, como la página de detalles del artículo, por ejemplo `/article/1`, `/article/2`, sus rutas son diferentes pero los componentes correspondientes son los mismos, por lo que el nombre de su componente es igual, como se mencionó anteriormente, con la inclusión de `keep-alive` solo se puede almacenar en caché basándose en el nombre del componente, por lo que esto es un problema. Actualmente hay dos soluciones:
- En lugar de utilizar la inclusión de keep-alive, este almacenará directamente todos los componentes en caché. De esta manera, se ayudara a la situación antes mencionada.
En [@/layout/components/AppMain.vue](https://github.com/adempiere/adempiere-vue/blob/master/src/layout/components/AppMain.vue) elimina el código relacionado con `include`. Por supuesto, usar keep-alive directamente también tiene desventajas. No puedes eliminar dinámicamente la caché. Solo puedes ayudar estableciendo un límite máximo de instancia de caché.
[issue](https://github.com/vuejs/vue/issues/6509)
- Utiliza un esquema de caché del navegador como localStorage, para controlar la caché.
## Affix <Badge text="v3.10.0+"/>
Si el atributo Affix se agrega a la ruta, `tag` quedará fija en `tags-view` (no se podrá quitar).
![](https://user-images.githubusercontent.com/8121621/52840303-cd5c9280-3133-11e9-928f-e2825eaab51b.png)
```js {14}
{
path: '',
component: Layout,
redirect: 'dashboard',
children: [
{
path: 'dashboard',
component: () => import('@/views/dashboard/index'),
name: 'Dashboard',
meta: {
title: 'dashboard',
icon: 'dashboard',
noCache: true,
affix: true
}
}
]
}
```
## Eliminar
De hecho, el [código fuente](<(https://github.com/vuejs/vue/blob/dev/src/core/components/keep-alive.js)>) de keep-alive no es complicado, y la lógica sigue siendo bastante clara. Antes de que el autor de `vue` corrigiera un error, no tuvo cuidado e hizo dos versiones para solucionarlo, por lo que si no hay ningún usuario que necesite la barra de navegación, se recomienda Eliminar esta función.
Primero encuentra
`@/layout/components/AppMain.vue` y elimina `keep-alive`
```html
<template>
<section class="app-main" style="min-height: 100%">
<transition name="fade-transform" mode="out-in">
<router-view></router-view> <!-- o también <router-view :key="key"/> -->
</transition>
</section>
</template>
```
Elimina el archivo `@/layout/components/TagsView.vue`. Luego, elimina la referencia a `TagsView` en los archivos `@/layout/components/index` y `@/layout/Layout.vue`. Finalmente, elimina el archivo `@/store/modules/tagsView`.

View File

@ -0,0 +1,11 @@
# Gitter
[Gitter](https://gitter.im/adempiere-vue/discuss)
<script>
export default {
mounted () {
window.open('https://gitter.im/adempiere-vue/discuss')
}
}
</script>

View File

@ -0,0 +1,11 @@
# Notas de la versión
[Notas de la versión](https://github.com/adempiere/adempiere-vue/releases)
<script>
export default {
mounted () {
window.open('https://github.com/adempiere/adempiere-vue/releases')
}
}
</script>

View File

@ -0,0 +1,65 @@
---
sidebarDepth: 3
---
# Clipboard
Here's the copy and paste based on [clipboard](https://github.com/zenorocha/clipboard.js)
This project provides two ways to use.
## Use directly
```html
<el-button @click='handleCopy(inputData,$event)'>copy</el-button>
```
```js
import clip from '@/utils/clipboard.js' // use clipboard directly
methods: {
handleCopy(text, event) {
clip(text, event)
}
}
```
First of all, import `clipboard.js` and set `click` function.
`clip()` The first parameter is the contents of the copy, the second parameter is the event event. Both parameters are required.
<br/>
<br/>
## v-directive
This project also encapsulates a `v-clipboard`.
```html
<el-button
v-clipboard:copy='inputData'
v-clipboard:success='clipboardSuccess'>
copy
</el-button>
```
```js
import clipboard from '@/directive/clipboard/index.js' // use clipboard by v-directive
directives: {
clipboard
},
methods: {
clipboardSuccess() {
this.$message({
message: 'Copy successfully',
type: 'success',
duration: 1500
})
}
}
```
`v-clipboard:copy`: the copy of the content.
`v-clipboard:success`: success callback function.

View File

@ -0,0 +1,104 @@
# Excel
## Excel Export
Import and export of Excel is implemented by relying on [js-xlsx](https://github.com/SheetJS/js-xlsx).
[Export2Excel.js](https://github.com/adempiere/adempiere-vue/blob/master/src/vendor/Export2Excel.js) is packaged on the on `js-xlsx` to facilitate exporting data.
### Use
Since `Export2Excel` depends not only on `js-xlsx` but also on `file-saver` and `script-loader`.
So you first need to install the following command:
```bash
npm install xlsx file-saver -S
npm install script-loader -S -D
```
Since `js-xlsx` size is still very large, the export function is not a very common function, so lazy loading is recommended when using it. The method of use is as follows:
```js
import('@/vendor/Export2Excel').then(excel => {
excel.export_json_to_excel({
header: tHeader, //Header Required
data, //Specific data Required
filename: 'excel-list', //Optional
autoWidth: true, //Optional
bookType: 'xlsx' //Optional
})
})
```
:::warning Warning <Badge text="v3.9.1+"/>
The compatibility code for Blob has been removed in the later versions of `v3.9.1+`. If you need to be compatible with very low-level browsers, you can manually introduce [blob-polyfill](https://www.npmjs.com/package/blob-polyfill) .
:::
### Params
| Params | Description | Type | Accepted Values | Default |
| --------- | --------------------------- | ------- | ----------------------------------------------------------------------------------- | ---------- |
| header | Export header of data | Array | / | [] |
| data | Exported specific data | Array | / | [] |
| filename | Export file name | String | / | excel-list |
| autoWidth | Whether the cell auto width | Boolean | true / false | true |
| bookType | Export file type | String | xlsx, csv, txt, [more](https://github.com/SheetJS/js-xlsx#supported-output-formats) | xlsx |
### Example
```js
import('@/vendor/Export2Excel').then(excel => {
const tHeader = ['Id', 'Title', 'Author', 'Readings', 'Date']
const data = this.list
excel.export_json_to_excel({
header: tHeader, //Header Required
data, //Specific data Required
filename: 'excel-list', //Optional
autoWidth: true, //Optional
bookType: 'xlsx' //Optional
})
})
```
- [Online Demo](https://adempiere.github.io/adempiere-vue/#/excel/export-excel)
- [Online Code](https://github.com/adempiere/adempiere-vue/blob/master/src/views/excel/export-excel.vue)
## Excel Import
Encapsulated [UploadExcel](https://github.com/adempiere/adempiere-vue/blob/master/src/components/UploadExcel/index.vue) Excel import component, support click and drag upload, also it is also Depends on `js-xlsx`.
It provides two callback functions:
- beforeUpload
You can make some special judgments before uploading. For example, if the size of the file is greater than 1 megabyte? If it is greater than 1 megabyte, it stops parsing and prompts an error message.
```js
beforeUpload(file) {
const isLt1M = file.size / 1024 / 1024 < 1
if (isLt1M) {
return true
}
this.$message({
message: 'Please do not upload files larger than 1m in size.',
type: 'warning'
})
return false
}
```
- onSuccess
A callback function that fires when parsing succeeds, which returns the header and content of the table.
```js
handleSuccess({ results, header }) {
this.tableData = results
this.tableHeader = header
}
```
- [Online Demo](https://adempiere.github.io/adempiere-vue/#/excel/upload-excel)
- [Online Code](https://github.com/adempiere/adempiere-vue/blob/master/src/views/excel/upload-excel.vue)

View File

@ -0,0 +1,77 @@
# Markdown Editor <Badge text="v3.9.3+"/>
Originally used [simplemde-markdown-editor](https://github.com/sparksuite/simplemde-markdown-editor) as the markdown editor, but this library has not been updated and maintained for a long time, and there is also the risk of xss. So after the <Badge text="v3.9.3+"/> version, use [tui.editor](https://github.com/nhnent/tui.editor) as the new editor. All the next documents are Based on tui.editor it. [More Content](https://github.com/nhnent/tui.editor).
## Props
| Name | Type | Default | Description |
| -------- | ------ | -------------------------- | ------------------------------------------------------------------------------------- |
| value | String | " " | This prop can change content of the editor. **If you using `v-model`, don't use it**. |
| options | Object | following `defaultOptions` | Options of tui.editor. This is for initailize tui.editor. |
| height | String | '300px' | This prop can control the height of the editor. |
| mode | String | 'markdown' | This prop can change mode of the editor. (`markdown`or `wysiwyg`) |
| language | String | 'en_US' | i18n |
```js
const defaultOptions = {
minHeight: '200px',
previewStyle: 'vertical',
useCommandShortcut: true,
useDefaultHTMLSanitizer: true,
usageStatistics: false,
hideModeSwitch: false,
toolbarItems: [
'heading',
'bold',
'italic',
'strike',
'divider',
'hr',
'quote',
'divider',
'ul',
'ol',
'task',
'indent',
'outdent',
'divider',
'table',
'image',
'link',
'divider',
'code',
'codeblock'
]
}
```
## Methods
- setValue
- getValue
- setHtml
- getHtml
## Example
```html
<template>
<markdown-editor v-model="content" />
</template>
<script>
import MarkdownEditor from '@/components/MarkdownEditor'
export default {
data() {
return {
content: '',
}
}
}
</script>
```
## Online Example
[link](https://adempiere.github.io/adempiere-vue/#/components/markdown)

View File

@ -0,0 +1,62 @@
# Pagination <Badge text="v3.9.2+"/>
Pagination component is mainly based on Element 'el-pagination' for the secondary packaging, and expanded the function of auto-scroll.
## Basic Usage
```html
<template>
<pagination
:total="total"
:page.sync="listQuery.page"
:limit.sync="listQuery.limit"
@pagination="getList" />
</template>
<script>
import Pagination from '@/components/Pagination'
export default {
components: { Pagination },
data() {
return {
total: 0,
listQuery: {
page: 1,
limit: 20
}
}
},
methods: {
getList() {
// Fetch data
}
}
}
</script>
```
## Attributes
| Attribute | Description | Type | Default |
| :---------: | :---------------------------------------------------------- | :-------: | :-------------: |
| total | total item count | Number | / |
| page | current page number, supports the .sync modifier | Number | 1 |
| limit | item count of each page, supports the .sync modifier | Number | 20 |
| page-sizes | options of item count per page | Number [] | 10, 20, 30, 50] |
| hidden | whether to hide | Boolean | false |
| auto-scroll | whether to automatically scroll to the top after pagination | Boolean | true |
Other attributes of the element's `el-pagination` support are also supported. See [Documentation](http://element.eleme.io/#/zh-CN/component/pagination) for details.
## Events
| Event Name | Description | Parameters |
| ---------- | ---------------------------------------- | ------------ |
| pagination | Triggered when the limit or page changes | {page,limit} |
## Source Code && Demo
- [Source Code](https://github.com/adempiere/adempiere-vue/blob/master/src/components/Pagination/index.vue)
- [Online Demo](https://adempiere.github.io/adempiere-vue/#/table/complex-table)

View File

@ -0,0 +1,65 @@
# Rich text editor
Rich text editor is a core part of management system, but at the same time is a place with lots of problems. In the process of selecting rich texts, I also walked a lot of detours. The common rich text editors in the market are basically used, and the finally chose [Tinymce](https://github.com/tinymce/tinymce).
Here is a brief introduction to the reasons why `tinymce` is recommended: `tinymce` is a veteran to do rich text company(Here also recommended `ckeditor`, is also a company that has been doing rich text, the new version is very good), its products have stood the test of the market, and it has detailed documentation and rich configuration. One of the keys to using rich text is to copy formatting. Before using a Korean text rich `summernote`, It wasted me a lot of time, very unfriendly. But `tinymce`'s formatting is pretty good. It also has a value-added feature is powerpaste, it is extremely powerful, support for copying everything from word or any other place. Extensibility is also critical for rich text. I use `tinymce` to write several plug-ins, learning costs and ease of study are good, and very easy to expand. The last point is that the documentation is very good, basically you want to get the configuration item, it has. Tinymce also supports on-demand loading, you can customize plugins through its official build page.
Let me analyze some of the other rich texts on the market:
- **[summernote](https://github.com/summernote/summernote)** Let me start with a rich text that I definitely would not recommend.It is inconsistent with many recognized default behaviors between others. And only for the use of a dialog feature, they import the bootstrap, A bunch of people protest. Formatting is also very bad. Do not use anyway! Do not use it! Do not use it!
- **[ckeditor](https://github.com/galetahub/ckeditor)** Ckeditor is also a veteran company to do rich text,
I used to use it in company project.This year, the 5.0 version of the UI has also become more beautiful, quite good, and it has the richest plugins. It's recommended that you try it.
- **[quill](https://github.com/quilljs/quill)** Is also a very hot rich text, the skin is very elegant. Writing a plug-in based on it is also very simple. The API design is very cool. The reason I did not choose it was because it was not good for operation picture and was hard to change. If there is no operation of the picture of the user, it is recommended.
- **[medium-_editor_](https://github.com/yabwe/medium-editor)** The famous medium rich text (unofficial produced), but the degree of completion is still not very good, scalability is not bad. However, I think most users still will not be used medium this way of writing.
- **[Squire](https://github.com/neilj/Squire)** A relatively light, rich text, compressed only 11.5kb, relative to other rich text is very small, recommended features is not complicated suggestion.
- **[UEditor](http://ueditor.baidu.com/website/index.html)** Not used in depth, only a simple project used in the angular1X, but ui really ugly, does not meet the aesthetic today, the official has also been a long time did not go with the new.
- **[slate](https://github.com/ianstormtaylor/slate)** A completely customizable framework for building rich text editors. Slate lets you build rich, intuitive editors like those in Medium, Dropbox Paper or Google Docs—which are becoming table stakes for applications on the web—without your codebase getting mired in complexity. Looks cool, after a chance I will practice in the project try it.
I listed a lot of rich text, but I didn't list any rich text related to vue, mainly because rich text is really more complex than thought. Also said in the previous article, in fact, encapsulation vue components is very convenient, there is no need to use someone else's package of things.
What kind of vue-quill vue-editor is just a simple package, no difficulty. You might as well encapsulate it yourself, and be a little more flexible and controllable. In addition vue really doesn't have any good rich text, unlike react has [draft](https://github.com/facebook/draft-js) produced by facebook, [editor](https://github.com/ory/editor) produced by ory. Vue doesn't have this product from a big company.
Of course, you can also choose some paid rich text editor, the author's own company has a project in the use of the [froala-editor](https://www.froala.com/wysiwyg-editor). Whether it is beautiful and easy to use are good, the company bought a professional version, $ 349 a year, the price is also very reasonable, but in fact save the cost of developer development may go far beyond the price.
## Tinymce
Here to briefly talk about the use of Tinymce in you own projects.
:::warning Deprecated
The current method of using the global reference. Code in: `static/tinymce` (The files in the static directory will not be build by webpack), import in index.html .And make sure it's in the order before your `app.js`!
:::
After <Badge text="v4.2.0+"/> will dynamic import tinymce by `CDN` .
If you want to change the cdn address or the version of tinymce, just find tinymce cdn in [@/components/Tinymce](https://github.com/adempiere/adempiere-vue/blob/master/src/components/Tinymce/index.vue) then modified it. It will be automatically injected into `index.html` via `dynamicLoadScript`.
> The current use of the npm installation 'Tinymce' method is more complex and has some problems (which may be used in the future). :space_invader:
**Usage**
Because rich text is not suitable for two-way data, so only watch the content changes once, and then will not be watch again. If later you need to change the rich text content.Can be set by `this.refs.xxx.setContent ()`.
The source code is also very simple, any other needs can be modified in `@/components/Tinymce/index.vue`.
```html
<tinymce :height="300" v-model="content" id='tinymce'></tinymce>
```
At present, the following attributes are provided, and there are requirements that can be added by themselves or an issue.
| Property | Description | Type | Default |
| :------: | :-------------------------: | :-----------: | :--------------------------------------: |
| id | Component unique identifier | String | Default to help you generate a unique id |
| value | Rich text content | String | Only monitor changes once |
| toolbar | Rich text toolbar | Array | [] |
| menubar | Rich text menubar | String | 'file edit insert view format table' |
| height | Rich text height | Number | 360 |
| width | Rich text width | Number String | / |
## tinymce-vue
The tinymce official also released the vue version of [tinymce-vue](https://github.com/tinymce/tinymce-vue), which has helped you package a lot of things, but at the same time it has reduced flexibility. If you are interested, you can study it by yourself.

View File

@ -0,0 +1,38 @@
---
sidebarDepth: 3
---
# Svg Icon
Global icon component: Svg Icon.
By default, the Svg Icon component is registered in [@/icons](https://github.com/adempiere/adempiere-vue/blob/master/src/icons/index.js#L6), and can be used anywhere in the project. All icons can be found in [@/icons/svg](https://github.com/adempiere/adempiere-vue/tree/master/src/icons/svg). You can add or remove the icon by yourself, and the icon will be imported automatically without manual operation.
## Usage
```html
<!-- use icon-class to setting name; use `class-name` to customizing class -->
<svg-icon icon-class="password" class-name='custom-class' />
```
## Change color
By default, `svg-icon` reads its parent color `fill: currentColor;`
You can change the parent `color` or directly `fill` color.
## Import from url <Badge text="v4.2.0+"/>
Support import `svg` from external url. E.g:
`<svg-icon icon-class="https://xxxx.svg" />`
## Size
If you are downloading an icon from [iconfont](https://www.iconfont.cn/), remember to use a tool such as Sketch to specify the size of the icon. Otherwise, the size of the icons in the project may not be uniform.
The icons used in this project are all 128\*128 size specifications.
:::tip
If you encounter the wrong color of the icon, you can refer to the [issue](https://github.com/adempiere/adempiere-vue/issues/330) for modification
:::

View File

@ -0,0 +1,108 @@
# Tree-Table
## Brief
This component only provides a solution for creating `TreeTable`. It is based on the `element-ui` table component. It uses the `row-style` method of `el-table` to determine whether the element needs to be hidden or displayed.
And this component makes full use of the features of the `vue` slot to make it user-friendly.
In `evel.js`, the `addAttrs` method adds several properties to the data, and `treeTotable` flattens the array. None of these operations will destroy the source data, just add properties.
## Props
| Attribute | Description | Type | Default |
| :--------------: | :----------------------------------------------------------- | :-----: | :------: |
| data | original display data | Array | [] |
| columns | column attribute | Array | [] |
| defaultExpandAll | whether to expand all nodes by default | Boolean | false |
| defaultChildren | specify which node object is used as the node's subtree | String | children |
| indent | horizontal indentation of nodes in adjacent levels in pixels | Number | 50 |
> Any of the `el-table` properties are supported, such as `border`, `fit`, `size` or `@select`, `@cell-click`. See the ʻel-table` documentation for details.
---
### Example
```html
<tree-table :data="data" :columns="columns" border>
```
#### data(**Required**)
```js
const data = [
{
name:'1'
children: [
{
name: '1-1'
},
{
name: '1-2'
}
]
},
{
name: `2`
}
]
```
#### columns(**Required**)
- label: text displayed in the header
- key: data.key will show in column
- expand: `true` or `false`
- checkbox: `true` or `false`
- width: column width 。such as `200`
- align: alignment `left/center/right`
- header-align: alignment of the table header `left/center/right`
```javascript
const columns = [
{
label: 'Checkbox',
checkbox: true
},
{
label: '',
key: 'id',
expand: true
},
{
label: 'Event',
key: 'event',
width: 200,
align: 'left'
},
{
label: 'Scope',
key: 'scope'
}
]
```
> The tree table component will generate a named slot based on the key property of columns. If you need to customize the column data, you can do it through the slot.
```html
<template slot="your key" slot-scope="{scope}">
<el-tag>level: {{ scope.row._level }}</el-tag>
<el-tag>expand: {{ scope.row._expand }}</el-tag>
<el-tag>select: {{ scope.row._select }}</el-tag>
</template>
```
## Events
Several methods are currently available, but only the `beta` version, which is likely to be modified later.
```js
this.$refs.TreeTable.addChild(row, data) //Add child elements
this.$refs.TreeTable.addBrother(row, data) //Add a sibling element
this.$refs.TreeTable.delete(row) //Delete the element
```
## Other
If you have other requirements, please refer to the [el-table](http://element-cn.eleme.io/#/en-US/component/table) api to modify the index.vue

View File

@ -0,0 +1,19 @@
# New <Badge text="v4.0.0+"/>
In daily work, the most common is to write modules and business components. When you open a new `view` or `component` every time you need to manually create a new `.vue`, create a `<template>`, `<script>`, `<style>`, or some problem.
So in the new version, based on [plop](https://github.com/amwmedia/plop), several basic templates are provided to facilitate the creation of the new `view` or`component`.
Execute the following command:
```bash
npm run new
```
![plop](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/5f8ea239-aaa5-4e91-9d09-ed56b33a110d.gif)
As shown in the previous gif, it is easy to generate the basic code snippet I want by simply pressing Enter several times. This is just a demonstration, you can customize the template according to your needs.
For additional template requirements, you can create a custom template by following the `plop` documentation and going to the `plop-templates` directory.
In fact, this feature is similar to what snippets do. If you think the configuration is too complicated, you can install a code fragment based on VSCode such as [Vue 2 Snippets](https://marketplace.visualstudio.com/items?itemName=hollowtree.vue-snippets).

View File

@ -0,0 +1,20 @@
# Svgo <Badge text="v3.9.0+"/>
This project provides svg compression processing optimization. Based on [svgo](https://github.com/svg/svgo).
```bash
npm run svgo
```
Many online downloads or svg exported by `Sketch` will have a lot of redundant and useless information, greatly increasing the size of svg. We can optimize it with `svgo`. For example, the following figure is an svg exported by `Sketch`
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/333edb6b-4b95-42f8-aa60-b8f42e516b52.jpg)
We can execute `npm run svgo`
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/e7b1324e-cd67-4306-aebf-f659bcc433cf.jpg)
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/006c4bb5-b2d1-447d-a1c9-a912cf5dee47.jpg)
Useless information is processed.
More detailed configuration can be configured in `/src/icons/svgo.yml`.

231
docs/guide/README.md Executable file
View File

@ -0,0 +1,231 @@
---
pageClass: getting-started
---
# Introduction
**Note: This documentation was forked from original project of [PanJiaChen](https://github.com/PanJiaChen/vue-element-admin-site). The honour is for him because started this big project. Any change after forked is maked from [ADempiere Team](https://github.com/adempiere/adempiere)**
[![vue](https://img.shields.io/badge/vue-2.6.10-brightgreen.svg)](https://github.com/vuejs/vue)
[![element-ui](https://img.shields.io/badge/element--ui-2.7.0-brightgreen.svg)](https://github.com/ElemeFE/element)
[![Build Status](https://travis-ci.org/adempiere/adempiere-vue.svg?branch=master)](https://travis-ci.org/adempiere/adempiere-vue)
[![license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/adempiere/adempiere-vue/blob/master/LICENSE)
[![GitHub release](https://img.shields.io/github/release/PanJiaChen/adempiere-vue.svg)](https://github.com/adempiere/adempiere-vue/releases)
[![donate](https://img.shields.io/badge/%24-donate-ff69b4.svg)](https://adempiere-vue.gitee.io/adempiere-vue-site/zh/donate)
[![GitHub stars](https://img.shields.io/github/stars/adempiere/adempiere-vue.svg?style=social&label=Stars)](https://github.com/adempiere/adempiere-vue)
[adempiere-vue](http://adempiere.github.io/adempiere-vue) is a production-ready front-end solution for admin interfaces. It based on [vue](https://github.com/vuejs/vue) and use the UI Toolkit [element-ui](https://github.com/ElemeFE/element).
It is a magical vue admin based on the newest development stack of vue, built-in i18n solution, typical templates for enterprise applications, lots of awesome features. It helps you build a large complex Single-Page Applications. I believe whatever your needs are, this project will help you.
:::tip
This project integrates a lot of features that you may not use, it will cause a lot of code redundancy. If your project does not pay attention to this issue, you can also directly develop it based on it.
Otherwise, you can use [vue-admin-template](https://github.com/adempiere/vue-admin-template).
- Integrated Solution: [adempiere-vue](https://github.com/adempiere/adempiere-vue)
- Basic Template: [vue-admin-template](https://github.com/adempiere/vue-admin-template)
- Desktop: [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
- Typescript: [vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template) (Credits: [@Armour](https://github.com/Armour))
- Others: [awesome-project](https://github.com/adempiere/adempiere-vue/issues/2312)
:::
<br/>
## Features
```
- Login / Logout
- Permission Authentication
- Page permission
- Directive permission
- Permission configuration page
- Two-step login
- Multi-environment build
- dev sit stage prod
- Global Features
- I18n
- Multiple dynamic themes
- Dynamic sidebar (supports multi-level routing)
- Dynamic breadcrumb
- Tags-view (Tab page Support right-click operation)
- Svg Sprite
- Mock data
- Screenfull
- Responsive Sidebar
- Editor
- Rich Text Editor
- Markdown Editor
- JSON Editor
- Excel
- Export Excel
- Upload Excel
- Visualization Excel
- Export zip
- Table
- Dynamic Table
- Drag And Drop Table
- Inline Edit Table
- Error Page
- 401
- 404
- Components
- Avatar Upload
- Back To Top
- Drag Dialog
- Drag Select
- Drag Kanban
- Drag List
- SplitPane
- Dropzone
- Sticky
- CountTo
- Advanced Example
- Error Log
- Dashboard
- Guide Page
- ECharts
- Clipboard
- Markdown to html
```
<br/>
## Preparation
You need to install [node](http://nodejs.org/) and [git](https://git-scm.com/) locally. The project is based on [ES2015+](http://es6.ruanyifeng.com/), [vue](https://cn.vuejs.org/index.html), [vuex](https://vuex.vuejs.org/zh-cn/), [vue-router](https://router.vuejs.org/zh-cn/), [vue-cli](https://github.com/vuejs/vue-cli) , [axios](https://github.com/axios/axios) and [element-ui](https://github.com/ElemeFE/element), all request data is simulated using [Mock.js](https://github.com/nuysoft/Mock).
Understanding and learning this knowledge in advance will greatly help the use of this project.
At the same time supporting a series of tutorial articles, how to build a complete background project from zero, suggest that you read these articles and then come to practice this project. But there's no English version yet.
- [手摸手,带你用 vue 撸后台 系列一(基础篇)](https://juejin.im/post/59097cd7a22b9d0065fb61d2)
- [手摸手,带你用 vue 撸后台 系列二(登录权限篇)](https://juejin.im/post/591aa14f570c35006961acac)
- [手摸手,带你用 vue 撸后台 系列三 (实战篇)](https://juejin.im/post/593121aa0ce4630057f70d35)
- [手摸手,带你用 vue 撸后台 系列四(vueAdmin 一个极简的后台基础模板)](https://juejin.im/post/595b4d776fb9a06bbe7dba56)
- [手摸手,带你用 vue 撸后台 系列五(v4.0 新版本)](https://juejin.im/post/5c92ff94f265da6128275a85)
- [手摸手,带你封装一个 vue component](https://segmentfault.com/a/1190000009090836)
- [手摸手,带你优雅的使用 icon](https://juejin.im/post/59bb864b5188257e7a427c09)
- [手摸手,带你用合理的姿势使用 webpack4](https://juejin.im/post/5b56909a518825195f499806)
- [手摸手,带你用合理的姿势使用 webpack4](https://juejin.im/post/5b5d6d6f6fb9a04fea58aabc)
::: tip
**This project does not support low-level browsers (such as ie). If you need to, please add polyfills yourself.**
:::
## Project Structure
This project has built the following templates, and have built a scaffold based on Vue, which should help you prototyping production-ready admin interfaces. It covers almost everything you need.
```bash
├── build # build config files
├── mock # mock data
├── plop-templates # basic template
├── public # pure static assets (directly copied)
│ │── favicon.ico # favicon
│   └── index.html # index.html template
├── src # main source code
│   ├── api # api service
│   ├── assets # module assets like fonts,images (processed by webpack)
│   ├── components # global components
│   ├── directive # global directive
│   ├── filters # global filter
│   ├── icons # svg icons
│   ├── lang # i18n language
│   ├── layout # global layout
│   ├── router # router
│   ├── store # store
│   ├── styles # global css
│   ├── utils # global utils
│   ├── vendor # vendor
│   ├── views # views
│   ├── App.vue # main app component
│   ├── main.js # app entry file
│ └── permission.js # permission authentication
├── tests # tests
├── .env.xxx # env variable configuration
├── .eslintrc.js # eslint config
├── .babelrc # babel config
├── .travis.yml # automated CI configuration
├── vue.config.js # vue-cli config
├── postcss.config.js # postcss config
└── package.json # package.json
```
## Getting Started
```bash
# clone the project
git clone https://github.com/adempiere/adempiere-vue.git
# install dependency
yarn
# develop
yarn dev
```
<br/>
This will automatically open [http://localhost:9527](http://localhost:9527).
If you see the following page then you have succeeded.
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/1bc334a6-32a8-4f29-a037-ac3f5ce32588.png)
We have built-in models, standard components, mock data, hot module reloading, state management, i18n, global router, etc. You can continue exploring other documents for more details on those topics.
<br/>
::: tip
**Suggestion** You can use `adempiere-vue` as a toolbox or as an integration solution repository, It is recommended to do secondary development on the basis of `vue-admin-template`, if you need any additional feature, you can copy from `adempiere-vue`.
:::
## Contribution
The repository of documentation is [adempiere-vue-site](https://github.com/erpcya/adempiere-vue-site) based on [vuepress](https://github.com/vuejs/vuepress) development.
There may be some spelling or translation errors in the course of writing this document. It is welcome to point out by issue or pr. After all, English is not my mother tongue.
[adempiere-vue](https://github.com/adempiere/adempiere-vue) is also continuing to iterate, summarize and summarize more features, and summarize the best practices of product templates/components/business scenarios in the middle and back office. This project is also very much looking forward to your participation and [feedback](https://github.com/adempiere/adempiere-vue/issues).
## Donate
If you think this project is useful, you can buy a glass of juice for the author :heart:
[Donate](/donate/)
## Browsers Support
Modern browsers and Internet Explorer 10+.
<!-- prettier-ignore -->
| [<img class="no-margin" src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img class="no-margin" src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img class="no-margin" src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img class="no-margin" src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
| --------- | --------- | --------- | --------- |
| IE10, IE11, Edge| last 2 versions| last 2 versions| last 2 versions
## Vue Ecosystem
**First understanding the things in these vue ecosystems will help you get started with this project.**
1. [Vue Router](https://router.vuejs.org/) Vue Router is the official router for Vue.js. It deeply integrates with Vue.js core to make building Single Page Applications with Vue.js a breeze.
2. [Vuex](https://vuex.vuejs.org/) Vuex is a state management pattern + library for Vue.js applications. It serves as a centralized store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion.
3. [Vue Loader](https://vue-loader.vuejs.org) Vue-loader is a loader for webpack that allows you to author Vue components in a format called Single-File Components (SFCs). The combination of webpack and vue-loader gives you a modern, flexible and extremely powerful front-end workflow for authoring Vue.js applications.
4. [Vue Server Renderer](https://ssr.vuejs.org/) Vue-server-renderer facilitates building of isomorphic or universal JavaScript applications which runs both on server and client side where majority of the application code is shared and reused.
5. [Vue Test Utils](https://vue-test-utils.vuejs.org/) Vue Test Utils is the official unit testing utility library for Vue.js.
6. [Vue Dev-Tools](https://github.com/vuejs/vue-devtools) Browser devtools extension for debugging Vue.js applications.
7. [Vue CLI](https://cli.vuejs.org/) Vue CLI is a full system for rapid Vue.js development. It aims to be the standard tooling baseline for the Vue ecosystem. It ensures the various build tools work smoothly together with sensible defaults so you can focus on writing your app instead of spending days wrangling with configurations.
8. [Vetur](https://github.com/vuejs/vetur) Vue tooling for VS Code. Write vue essential plugins under VS Code.

View File

@ -0,0 +1,80 @@
# CDN
You can analyze the results of the `webpack` package by executing `npm run preview -- --report` and observe the size of each static resource. You can find that the most occupied space is the dependence of third parties. Such as `vue`, `element-ui`, `ECharts`, etc.
You can use the `CDN` link to introduce these third-party libraries, which can greatly increase the speed of the build (the resources introduced through the CDN are not packaged by webpack). If your project does not have its own `CDN` service, use some third-party `CDN` services, such as [unpkg](https://unpkg.com/), etc. It is a good choice, it has provided free Resource acceleration. At the same time, it provides cache optimization. Since your third-party resources are introduced in `html` through `script`, its cache update strategy is controlled by you manually, eliminating the need to optimize the cache strategy.
::: tip
Many articles say that the use of `CDN` can greatly reduce the size of the code, which is impossible. Although the packaged `bundle` is small. But that part of the code was just removed by you, and it was introduced using the `CDN` method. The most efficient solution you want to reduce the size is to enable `GZIP`.
:::
## I personally do not use `CDN`
There is no problem with the temporary build speed, and there is no need to strip some of the third-party dependencies separately. Using `CDN` equals some third-party dependent versions you control through `package.json`, some dependencies require manual maintenance, adding some maintenance costs. At present, the webpack-based `optimization.splitChunks` has been optimized for the caching of resources, and the caching of static resources has been done very well. And all current static resources will be uploaded to their own `CDN` service, there is no need to use a third-party `CDN` service.
**Of course, all optimizations need to be adjusted in conjunction with their specific business!** If you feel that the use of `CDN` is beneficial for your project, you can follow these steps:
## Way of use
First find `vue.config.js`, add `externals` to make `webpack` not package `vue` and `element`
```js
externals: {
vue: 'Vue',
'element-ui':'ELEMENT'
}
```
Then configure the `CDN` of those third-party resources, please pay attention to the order.
```js
const cdn = {
css: [
// element-ui css
'https://unpkg.com/element-ui/lib/theme-chalk/index.css'
],
js: [
// vue must at first!
'https://unpkg.com/vue/dist/vue.js',
// element-ui js
'https://unpkg.com/element-ui/lib/index.js'
]
}
```
Then inject it into `index.html` via `html-webpack-plugin`:
```js
config.plugin('html').tap(args => {
args[0].cdn = cdn
return args
})
```
Find `public/index.html`. Inject css and js in turn through your configured `CND Config`.
```html
<head>
<!-- inject css-->
<% for(var css of htmlWebpackPlugin.options.cdn.css) { %>
<link rel="stylesheet" href="<%=css%>">
<% } %>
</head>
<!-- inject js -->
<% for(var js of htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%=js%>"></script>
<% } %>
```
There is also a small detail. If you use the global object method to introduce vue, you don't need to manually Vue.use(Vuex), it will be mounted automatically. [issue](https://github.com/vuejs/vuex/issues/731)
Complete [code modification](https://github.com/adempiere/vue-admin-template/commit/eaaa3c1ddadd114451a1a83e042f1fc56a9809a1)
Finally you can use `npm run preview -- --report` to see the effect as shown:
![](https://camo.githubusercontent.com/0c5bdc47aeaecc340b9a5a88325b49885538bf90/68747470733a2f2f70616e6a69616368656e2e6769746875622e696f2f696d616765732f656c656d656e742d63646e2e706e67)
::: tip
By the same token, other third-party dependencies can be handled in the same way(such as `vuex`, `vue-router`, etc.). Of course, you can also choose to use [DLLPlugin](https://webpack.docschina.org/plugins/dll-plugin/) to handle third-party dependencies to optimize the build.
:::

133
docs/guide/advanced/chart.md Executable file
View File

@ -0,0 +1,133 @@
# Chart
Managing background charts is also a common requirement. The chart here only recommends ECharts, full-featured, community demo is also rich [gallery](http://gallery.echartsjs.com/explore.html)。
I still have that point of view. Most plug-ins recommend that use vue for packaging by yourself. It's really simple. ECharts supports the import of webpack, you can import the whole ECharts `var echarts = require ('echarts')` However, ECharts is not small, if you use only a small part of the features or chart type, then recommend on-demand import.
```js
// Import on demand -- import ECharts main module
var echarts = require('echarts/lib/echarts')
// Import bar
require('echarts/lib/chart/bar')
// Import tooltip&title
require('echarts/lib/component/tooltip')
require('echarts/lib/component/title')
// Import all ECharts module
var echarts = require('echarts')
```
[Use ECharts with webpack](https://ecomfe.github.io/echarts-doc/public/en/tutorial.html#Use%20ECharts%20with%20webpack)
[Include ECharts charts and components on demand](https://ecomfe.github.io/echarts-doc/public/en/tutorial.html#Use%20ECharts%20with%20webpack)
Next we will declare the initialization of ECharts in vue. Because ECharts initialization must be bound to dom, we can only initialize it during vue's mounted lifetime.
```js
mounted() {
this.initCharts();
},
methods: {
initCharts() {
this.chart = echarts.init(this.$el);
this.setOptions();
},
setOptions() {
this.chart.setOption({
title: {
text: 'ECharts 入门示例'
},
tooltip: {},
xAxis: {
data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"]
},
yAxis: {},
series: [{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}]
})
}
}
```
It's that simple, ECharts is configured, at this point you want to say that my data is obtained remotely, or how do I dynamically change the configuration of ECharts? We can trigger the setOptions method with watch
```js
// The first watch options change Using the depth of vue watcher, options are re-setOption
watch: {
options: {
handler(options) {
this.chart.setOption(this.options)
},
deep: true
},
}
// The second only watch data changes trigger ECharts only when the data changes
watch: {
seriesData(val) {
this.setOptions({series:val})
}
}
```
In fact, they are all similar, or they must be combined with their own business. There is no difference between using ECharts in peacetime.
## Demo
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/137aeadd-ad0e-4b21-badd-c53f96b7482b.gif)
::: tip Code
`@/views/dashboard/admin/components`
:::
## ECharts chart width is displayed incorrectly?
Sometimes you put ECharts in `el-tab` or`el-dialog`, and you will find that the width of the chart will be displayed incorrectly. As shown below:
<img :src="$withBase('/images/ECharts-width.png')" alt="ECharts-width.png" width="500px">
Because ECharts itself is not adaptive, you need to manually call its `.resize ()` method when the width of your parent container changes.
For example, `el-tab`, you can listen to the`change` event, and call the `.resize ()` method after finding the chart when the change occurs.
```html
<template>
<el-tabs v-model="active" @tab-click="handleClick">
<el-tab-pane label="用户管理" name="first">
<Chart ref="Chart" />
</el-tab-pane>
<el-tab-pane label="配置管理" name="second">配置管理</el-tab-pane>
<el-tab-pane label="角色管理" name="third">角色管理</el-tab-pane>
<el-tab-pane label="定时任务补偿" name="fourth">定时任务补偿</el-tab-pane>
</el-tabs>
</template>
<script>
export default {
data() {
return {
active: 'second'
};
},
watch: {
active(val) {
this.$nextTick(() => {
this.$refs.Chart.resize();
}
}
},
methods: {
handleClick(tab, event) {
console.log(tab, event);
}
}
};
</script>
```
It is relatively simple to put the chart in the `el-dialog`, as long as the init chart is displayed after the dialog appears.
## Others
Of course there are many other libraries in the community, such as [d3](https://github.com/d3/d3) , [Chart.js](https://github.com/chartjs/Chart.js) , [chartist-js](https://github.com/gionkunz/chartist-js). The packaging methods are almost the same, and they are no longer here.

20
docs/guide/advanced/cors.md Executable file
View File

@ -0,0 +1,20 @@
# Cors
The most question be asked is still about `cross-domain` issues. In fact, the `cross-domain` issue is really not a very difficult question to solve. Here I will briefly summarize several `cross-domain` solutions I recommend.
The most recommended way is `cors`, full name is `Cross Origin Resource Sharing`. This solution does not make any difference to the front-end write request as usual. The workload is basically on the back-end. For each request, the browser must first send a pre-request as `OPTIONS`, to know the server-side HTTP method supported for cross-source requests. After confirming that the server allows the cross-source request, then send the real request with the actual HTTP request method. Details [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)
The recommended reason is: as long as the first time is configured, no matter how many API interfaces and projects, they can be directly reused, and the cross-domain problem can be solved once and for all, and it can be conveniently used in both the development environment and the formal environment.
But there are always some back-end developers who think `cors` is too much `trouble`, they don't want to help the front end to solve cross-domain issues. That pure front-end is also has solutions.
In `dev` environment, you can use webpack `proxy`, it is also very easy to use。 It's recommended that you look at the [document](https://www.webpackjs.com/configuration/dev-server/#devserver-proxy) and we're not going to discuss it here. Some of the author's personal projects use this method
But this method can not used in the `production` environment. In `production` environment, you need to use `nginx` reverse proxy. Whether `proxy` or `nginx`, the principle is the same. Solve the cross-domain issues by building a transit server to forward requests.
| development | production |
| :---------: | ---------- |
| cors | cors |
| proxy | nginx |
Here I only recommend these two ways to cross-domain, there are many other cross-domain methods but not recommended.

98
docs/guide/advanced/error.md Executable file
View File

@ -0,0 +1,98 @@
# Error Handling
## Page
**404**
Page-level error handling is handled uniformly by the `vue-router`. All pages that do not match the correct route will advance to the `404` page.
```js
{ path: '*', redirect: '/404' }
```
::: warning
One thing that needs special attention here is that the `404` page must be loaded last. If you put `404` in the constantRoutes , then the following page will be blocked to `404`. See the problem for details [addRoutes when you've got a wildcard route for 404s does not work](https://github.com/vuejs/vue-router/issues/1176)
:::
**401**
Permission control is done in `@/permission.js`. All users who do not have permission to access this route will be redirected to the `401` page.
<br/>
## Request
All the requests in the project will go through the axios instance created in `@/utils/request.js`. [code](https://github.com/adempiere/adempiere-vue/blob/master/src/utils/request.js)。
You can use the `service.interceptors.response`, the response interceptor to harmonize different status codes according to your actual business or to perform error handling according to custom code. Such as:
```js
service.interceptors.response.use(
response => {
/**
* The code is non-20000 error-free
*/
const res = response.data
if (res.code !== 20000) {
Message({
message: res.data,
type: 'error',
duration: 5 * 1000
})
// 50008: illegal token; 50012: other client logged in; 50014: Token expired;
if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
MessageBox.confirm(
'你已被登出,可以取消继续留在该页面,或者重新登录',
'确定登出',
{
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
store.dispatch('FedLogOut').then(() => {
location.reload() // 为了重新实例化vue-router对象 避免bug
})
})
}
return Promise.reject('error')
} else {
return response.data
}
},
error => {
console.log('err' + error) // for debug
Message({
message: error.message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
```
Since all requests return a `promise`, you can also pass a `catch` error for each request, which allows for separate processing.
```js
getInfo()
.then(res => {})
.catch(err => {
xxxx
})
```
## Coding
This project also does code-level error handling. If you enable `eslint`, you will be prompted for errors when writing code. Such as:
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/b037f47c-1f7b-487f-bb05-32e7300767d2.png)
Of course there are many errors that cannot be checked by `eslint`, vue also provides global error handling hooks[errorHandler](https://vuejs.org/v2/api/#errorHandler). The project also made a corresponding error collection.
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/360e4842-4db5-42d0-b078-f9a84a825546.gif)
::: tip
Listening error: [@/errorLog.js](https://github.com/adempiere/adempiere-vue/blob/master/src/errorLog.js)
Error display component: [@/components/ErrorLog](https://github.com/adempiere/adempiere-vue/blob/master/src/components/ErrorLog/index.vue)
:::

77
docs/guide/advanced/eslint.md Executable file
View File

@ -0,0 +1,77 @@
# ESLint
Whether it's a multi-person collaboration or personal projects, code specifications are important. It can not only avoids basic syntax errors, but also ensures the readability of the code.
## Config
All configuration files are in [.eslintrc.js](https://github.com/adempiere/adempiere-vue/blob/master/.eslintrc.js).
The basic eslint rules of this project is based on the official eslint rules of vue [eslint-config-vue](https://github.com/vuejs/eslint-config-vue) but made minor changes. You can customize your configuration according to your needs.
Such as: my personal or project team is accustomed to using two spaces, but you may feel that the four spaces are more pleasing, and you can make the following changes.
Enter the project of `.eslintrc.js`, find `indent`,and then set it to `4` 。There are a variety of configuration information, see details [ESLint Document](https://eslint.org/docs/rules/)。
After [v3.8.1](https://github.com/adempiere/adempiere-vue/releases/tag/v3.8.1), [eslint-plugin-vue](https://github.Com/vuejs/eslint-plugin-vue) has been added to better verify vue related code.
By default, the most restrictive config `plugin:vue/recommended` is used to verify the code. If you think it is too strict, you can modify it yourself.
```js
// https://github.com/adempiere/adempiere-vue/blob/master/.eslintrc.js
module.exports = {
extends: ['plugin:vue/recommended', 'eslint:recommended']
//You can change it to extends: ['plugin:vue/essential', 'eslint:recommended']
}
```
## Cancel ESLint
If you don't want to use ESLint (not recommended for cancellation), just find the [vue.config.js](https://github.com/adempiere/adempiere-vue/blob/master/vue.config.js) file.
Make the following settings `lintOnSave: false`.
## Configure ESLint in vscode
Sharp tools make good work! Personally recommend eslint+vscode to write VUE, there is definitely a very cool
![eslintGif.gif](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/e94a76df-6dc0-4c15-9785-28b553a163e9.png)
<br/>
Every time you save your code, vscode will be able to mark red areas that do not conform to the eslint rules, and make some simple self-fixes at the same time. The installation steps are as follows:
First install the eslint plugin
![eslint1.png](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/72f126cb-09eb-4b27-b02e-65e79eb76220.png)
After we have installed ESLint, we back to VSCode to set up . Go to `Code` > `Preferences` > `Settings` and add the following configuration.
```json
{
"files.autoSave": "off",
"eslint.validate": [
"javascript",
"javascriptreact",
"vue-html",
{
"language": "vue",
"autoFix": true
}
],
"eslint.run": "onSave",
"eslint.autoFixOnSave": true
}
```
Everyone and the team have their own code specification, unification is good, to create their own eslint rules and upload it to the npm will be fun. Such as ElemeFE [config](https://www.npmjs.com/package/eslint-config-elemefe) or Vue official [config](https://github.com/vuejs/eslint-config-vue).
[vscode plugin and configuration recommendations](https://github.com/varHarrie/Dawn-Blossoms/issues/10)
## More configuration
Since this project is built based on `vue-cli`, more configuration can be found in the official [documentation](https://cli.vuejs.org/en/config/#lintonsave)
## Auto fix
```bash
npm run lint -- --fix
```
Running the above command, eslint will automatically fix some simple errors.

78
docs/guide/advanced/git-hook.md Executable file
View File

@ -0,0 +1,78 @@
# Git Hooks
Programmers with engineering literacy will pay attention to coding standards, and Code Linting (Lint) is an important means to ensure code specification and consistency.
What are the benefits of using `Lint`? In my opinion, it has at least the following three points:
- Fewer bugs
- With higher development efficiency, Lint can easily find low-level, obvious errors.
- Higher code readability
Many times our `lint` check is placed in the continuous integration phase, the approximate process is as follows:
> Code Submission --> Run CI Found Problem (Remote) --> Local Fix Issue --> Resubmit --> Pass Check (Remote)
But there is a problem with this. Our `CI` (continuous integration) often doesn't just do `Lint` work, it also has many other tasks (such as packaging files, static resources uploaded to CDN, etc.), which leads to It's a special waste of time, it may take a few minutes for you to find the problem, or sometimes you don't find your `CI` is failed.
Common process: write the code locally, submit, start running lint, find that it does not pass, modify the code locally, submit it, wait for the result of CI, and repeat the previous operation if there are any problems.
## husky
The most effective solution is to put the `Lint` checksum locally. The common practice is to use [husky](https://github.com/typicode/husky) or [pre-commit](https://github.com/observing/pre-commit) Do a `Lint` check before committing locally.
> Of course, if you use `vue-cli@3` when creating your project, you can also use its built-in [yorkie](https://github.com/yyx990803/yorkie), which is based on `husky`, but Changed the interface. But here we still use `husky` as an example.
```bash
# Note: Our examples are all 1.3.1+ versions!
npm install husky -D -S
```
Then modify `package.json` to add the configuration:
```json
"husky": {
"hooks": {
"pre-commit": "eslint --ext .js,.vue src"
}
}
```
Finally try the `Git` submission and you will receive feedback soon:
```
git commit -m "Keep calm and commit"
```
But there is a problem. In my this git submission, I may have only modified one file. For example, I modified the content of `foo.js`, but it will still check all the '.js' files in `src`. It is very unfriendly. Every time I submit the code I wrote, I have to solve the other person's code lint problem first, then I can submit the code smoothly, and when the project is big, the inspection speed will become more and more slow.
## lint-staged
To solve the pain points above, you need to use [lint-staged](https://github.com/okonet/lint-staged). It will only check to check what you submitted or what you modified.
```bash
npm install lint-staged -D -S
```
Then, modify the package.json configuration:
```json
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"src/**/*.{js,vue}": [
"eslint --fix",
"git add"
]
}
```
As configured above, each time it will only check your local configuration for the `eslint` rule (this see the document [ESLint](eslint.md)) before your local `commit`, if it meets the rules, it will be submitted successfully. If it does not match, it will automatically execute `eslint --fix` to try to help you fix it automatically. If the repair is successful, it will help you to submit the repaired code. If it fails, you will be prompted with an error, and you will be allowed to submit the code only after you fix it.
## To sum up
The best `lint` specification process is to recommend team members to configure `eslint` in their own editor, and turn on the `eslint-loader` error in webpack, so the editor can help you automatically fixed some simple errors when you write. At the same time, it can obviously remind you of the code that does not meet the `lint` specification. See [ESLint](eslint.md) for details on this.
But this is not mandatory. Some team members or newly arrived interns have not configured the lint rule in the editor or ignored the error in the command line. In this case, you need to configure the mandatory pre-commit. Check that everything submitted to the remote repository is in compliance with the team's specifications.

96
docs/guide/advanced/i18n.md Executable file
View File

@ -0,0 +1,96 @@
# I18n
This project is a collection of internationalized i18n solutions. Implemented via [vue-i18n](https://github.com/kazupon/vue-i18n).
Since the project's ui framework uses `element`, internationalization also needs to be internationalized.
[code](https://github.com/adempiere/adempiere-vue/blob/master/src/lang/index.js).
At the same time, the current `lang` language save in the `cookie`, and the last language setting can be remembered for opening the page next time.
## Global lang
Code: [@/lang](https://github.com/adempiere/adempiere-vue/tree/master/src/lang)
Currently set English and Chinese languages.
Meanwhile, import a language package in `@/lang/index.js` for `element-ui`.
## Async lang
There are some langs that are needed for specific pages, such as the `@/views/i18n` page, you can use async lang.
```js
import local from './local'
this.$i18n.mergeLocaleMessage('en', local.en)
this.$i18n.mergeLocaleMessage('zh', local.zh)
```
# Use $t in js
If you use a component such as `select`, its value comes through `v-for`, such as:
```html
<el-select v-model="value">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"/>
</el-select>
```
```js
this.options = [
{
value: '1',
label: this.$t('i18nView.one')
},
{
value: '2',
label: this.$t('i18nView.two')
},
{
value: '3',
label: this.$t('i18nView.three')
}
]
```
In this case, i18n will only be executed once, because `this.options` in js will only be executed once during `created`, and its data will not change as your local `lang` changes, so You need to manually reset `this.options` when the `lang` changes.
```js
export default {
watch: {
lang() {
this.setOptions()
}
},
methods: {
setOptions() {
this.options = [
{
value: '1',
label: this.$t('i18nView.one')
},
{
value: '2',
label: this.$t('i18nView.two')
},
{
value: '3',
label: this.$t('i18nView.three')
}
]
}
}
}
```
## Remove i18n
In `src/main.js` remove `import i18n from './lang'` and delete `src/lang` folder.
And remove `this.$t('route.xxxx')` in `src/layout/components/Levelbar``src/layout/components/SidebarItem``src/layout/components/TabsView` or others.
After the <Badge text="v4.1.0+"/> version, the default master will no longer provide i18n. Because most users are not need i18n, the removal of i18n workload is quite large.
If you have i18n requirements, please use [i18n Branch](https://github.com/adempiere/adempiere-vue/tree/i18n), which is updated synchronously with master.

49
docs/guide/advanced/icon.md Executable file
View File

@ -0,0 +1,49 @@
# Icon
If you do not find the desired icon in the [Icon](https://github.com/adempiere/adempiere-vue/tree/master/src/icons/svg) of this project, you can select and generate your own business icon library on [iconfont.cn](http://iconfont.cn/)and use it again. Or other svg icon website, download svg and put it in this folder.
## Generate icon library code
First, search for and find the icon you need, and collect it into your shopping cart. In the shopping cart, you can add the selected icon to the project (if not, create a new one), and the subsequent generated resources/code are It is based on the dimension of the project.
> If you already have a design draft, just need to generate the relevant code, you can upload your icon, and then do the above operation.
<img width="600" alt="账户相关布局" src="https://gw.alipayobjects.com/zos/rmsportal/jJQYzRyqVFBBamUOppXH.png" />
<br />
** This project now supports and recommends separate export of svg usage. Download method as shown below:**
<img width="600" src="https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/1f8b1e56-cfd9-4ef7-a0aa-dfb0c2883aa3.gif" />
<br />
After the download is complete, the downloaded .svg file is automatically imported after it is placed in the `@/icons/svg` folder.
## How to use
```js
<svg-icon icon-class="password" /> // icon-class is the icon's name usage
```
[Component](/feature/component/svg-icon.md)
## Change color
`svg-icon` reads its parent's color `fill: currentColor;' by default.
You can change the parent's `color` or change the color of `fill` directly.
:::tip
If you encounter an incorrect icon color, you can refer to this[issue](https://github.com/adempiere/adempiere-vue/issues/330)
:::
## Detailed articles
[手摸手,带你优雅的使用 icon](https://juejin.im/post/59bb864b5188257e7a427c09)
## Currently available icons
[src/icons/svg](https://github.com/adempiere/adempiere-vue/tree/master/src/icons/svg)
Online [Preview Address](https://adempiere.github.io/adempiere-vue/#/icon/index)

View File

@ -0,0 +1,112 @@
# Lazy Loading Routes
When you package an application, the Javascript package becomes very large, affecting the page load. If we can split the components corresponding to different routes into different code blocks and then load the corresponding components when the route is accessed, this will be more efficient.
Combining Vue's [async component feature](https://vuejs.org/v2/guide/components-dynamic-async.html#Async-Components) and webpack's [code splitting feature](https://webpack.js.org/guides/code-splitting/), it's trivially easy to lazy-load route components.
```js
const Foo = () => import('./Foo.vue')
```
<br>
**When you think your page's hot reload is slow, you need to look down ↓**
## Differentiating development and production environments
**[This solution has been eliminated]**
When you have more and more pages in your project, using `lazy-loading` in the development environment becomes less appropriate, and every change of code that triggers a hot update becomes very slow. Therefore, it is recommended to only use the lazy loading function in the build environment.
**Development:**
```js
// vue-loader at least v13.0.0+
module.exports = file => require('@/views/' + file + '.vue').default
```
**Note here that this method only supports `vue-loader at least v13.0.0+`**[adempiere-vue/issues/231](https://github.com/adempiere/adempiere-vue/issues/231)
Production
```js
module.exports = file => () => import('@/views/' + file + '.vue')
```
## Elimination reason
Of course, there are some side effects of writing this way. due to
> Every module that could potentially be requested on an import() call is included. For example, import(./locale/${language}.json) will cause every .json file in the ./locale directory to be bundled into the new chunk. At run time, when the variable language has been computed, any file like english.json or german.json will be available for consumption.
::: tip
The user can measure whether to adopt this method according to the business situation. If your project is not large and you can also accept the local development hot update speed. You can continue to use lazy loading to avoid this side effect in all environments.
:::
## New Plan
Use `babel plugins` [babel-plugin-dynamic-import-node](https://github.com/airbnb/babel-plugin-dynamic-import-node).
It only does one thing by converting all `import()` to `require()`, so that all asynchronous components can be import synchronously using this plugin. Combined with the babel environment variable [BABEL_ENV](https://babeljs.io/docs/usage/babelrc/#env-option), let it only work in the development environment, in the development environment will convert all import () into require ().
This solution to solve the problem of repeated packaging before, while the invasiveness of the code is also very small, you usually write routing only need to follow the lazy loading method of the [official document](https://router.vuejs.org/guide/advanced/lazy-loading.html) routing on it, the other are handed to the handle of the cable, When you don't want to use this program, just remove it from Babel's plugins.
**Code:**
First add `BABEL_ENV` to `package.json`
```json
"dev": "cross-env BABEL_ENV=development webpack-dev-server --inline --progress --config build/webpack.dev.conf.js"
```
Then `.babelrc` can only include the `babel-plugin-dynamic-import-node` `plugins` and make it work only in the `development` mode.
```json
{
"env": {
"development": {
"plugins": ["dynamic-import-node"]
}
}
}
```
After that, you're done. Routing can be written as usual.
```js
{ path: '/login', component: () => import('@/views/login/index')}
```
[Related code changes](https://github.com/adempiere/adempiere-vue/pull/727)
## vue-cli@3 [The plan has been eliminated]
`adempiere-vue@4` has been modified to build based on `vue-cli` in the new version. So in the new version you just need to set `VUE_CLI_BABEL_TRANSPILE_MODULES:true` in the `.env.development` environment variable configuration file, specifically [code](https://github.com/adempiere/adempiere-vue/blob/master/.env.development).
Its implementation logic and principle are the same as before, it based on `babel-plugin-dynamic-import-node`.The only thing you need to set a variable in `vue-cli` is to borrow the default configuration of `vue-cli`. By reading [source code](https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/babel-preset-app/index.js), `vue-cli` will pass `VUE_CLI_BABEL_TRANSPILE_MODULES`,this environment variable to distinguish whether to use `babel-plugin-dynamic-import-node`, so we only need to set it to true. Although its original intention was for unit testing, it just met our needs.
### Elimination reason
In the era of `vue-cli@3`, using `VUE_CLI_BABEL_TRANSPILE_MODULES` is ok, but it is actually fragile, as in `vue-cli@4`, vue-cli introduces `babel-plugin-dynamic-import-node The logic of`has changed, it needs to be `VUE_CLI_BABEL_TRANSPILE_MODULES` and `VUE_CLI_BABEL_TARGET_NODE` to be true at the same time, so as long as the judgment logic of vue-cli changes, we need to make corresponding changes, or be very passive and coupled . So in the `vue-cli@4` version, we no longer set it by `VUE_CLI_BABEL_TRANSPILE_MODULES: true`, but by manually introducing `'babel-plugin-dynamic-import-node'`, see the next section for details.
## vue-cli@4
1. No need to configure `VUE_CLI_BABEL_TRANSPILE_MODULES = true` in the `.env.development` file, just delete it.
2. Run `npm install babel-plugin-dynamic-import-node -S -D`
3. The way to add the dynamic-import-node plugin in `babel.config.js`, see the next section for details.
```js
module.exports = {
presets: ['@vue/cli-plugin-babel/preset'],
env: {
development: {
plugins: ['dynamic-import-node']
}
}
}
```
## Improve
`webpack5` is about to be released, greatly improving the speed of packaging and compiling. After that, it may not need to be so complicated at all. More page hot updates can be very fast, and the solution mentioned above is completely unnecessary.

View File

@ -0,0 +1,56 @@
# Node Sass to Dart Sass
Before `v4.3.0`, this project was built based on `node-sass`, but `node-sass` low-level dependencies [libsass](https://github.com/sass/libsass), resulting in many users installing Especially difficult for Windows users, it forces users to install `python2` and `Visual Studio` in the `windows` environment to compile successfully.
So in order to solve this problem, this project was modified to build `dart-sass` in [v4.3.0](https://github.com/adempiere/adempiere-vue/pull/3040), it can guarantee performance Under the premise of greatly simplifying the user's installation costs. Through this [issue](https://github.com/adempiere/adempiere-vue/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc) the relevant comments below can be known, install` Node-sass is such a troublesome thing.
There is a more important reason for choosing to use dart-sass here. Officially, `sass` has taken dart-sass as the main development direction in the future. Any new features will be supported first, and it It has been running steadily in the community for a long time, and there are basically no pits. The main reason why dart-sass is easy to install is because it will be compiled into pure js, so that it can be used directly in the node environment. Although its running speed will be slower than that based on [libsass](https://github.com/sass/libsass), the difference in these speeds is almost negligible. The entire community is now embracing `dart-sass`, and we have no reason to refuse! And it does greatly simplify the user's installation costs.
Currently, `vue-cli` will also prefer to use `dart-scss` by default when selecting `sass` preprocessing, related: [pr](https://github.com/vuejs/vue-cli/pull/3321)
Related instructions can be found in this article: [Announcing Dart Sass](https://sass-lang.com/blog/announcing-dart-sass)
Specific `dart-sass` performance evaluation can be seen: [Perf Report](https://github.com/sass/dart-sass/blob/master/perf.md)
## Upgrade plan
The upgrade is also very simple, requiring only two steps
```bash
npm uninstall node-sass
npm install sass -S -D
```
The upgrade can also be seen in detail: [Pull Request](https://github.com/adempiere/adempiere-vue/pull/3040) is simple and only requires two steps
## Not compatible
One thing to note after replacing `node-sass` is that it no longer supports the `/deep/` writing style of `sass` before, and it needs to be changed to the writing style of `::v-deep`. Related: [issue](https://github.com/vuejs/vue-cli/issues/3399)
Concrete demo:
```css
.a {
/deep/ {
.b {
color: red;
}
}
}
/* change into */
.a {
::v-deep {
.b {
color: red;
}
}
}
```
Regardless of whether you use `dart-sass` or not, I suggest you use `::v-deep` notation, which is not only compatible with the css `>>>` notation, but also compatible with sass `/deep/` . And it's the way of writing specified in [vue 3.0 RFC](https://github.com/vuejs/rfcs/blob/scoped-styles-changes/active-rfcs/0023-scoped-styles-changes.md).
And the original writing of `/deep/` itself was abandoned by Chrome. You can often find a warning in the console that Chrome reminds you not to use `/deep/`.
More: [scope css writing](https://vue-loader.vuejs.org/guide/scoped-css.html)

View File

@ -0,0 +1,43 @@
## Style Guide
The style guide for this project is mainly based on the official [style guide](https://vuejs.org/v2/style-guide/index.html) . It is recommended to read the guide before you start using the project, which will help you write more standardized and unified code. Most of these rules are also configured in [eslint-plugin-vue](https://github.com/vuejs/eslint-plugin-vue). When the rules are not followed, will throw an error. For details, see [eslint](./eslint.md) section.
Of course, there are some special specifications that cannot be verified by eslint. You need to pay attention to yourself and follow. The most important thing is the naming rules for files. Take the example of `adempiere-vue` here.
## Component
All `Component` files start with uppercase (PascalCase), which is also official [recommended](https://vuejs.org/v2/style-guide/index.html#Single-file-component-filename-casing-strongly-recommended)。
But except for `index.vue`.
Example:
- `@/components/BackToTop/index.vue`
- `@/components/Charts/Line.vue`
- `@/views/example/components/Button.vue`
## JS files
All `.js` files follow `kebab-case`.
Example:
- `@/utils/open-window.js`
- `@/views/svg-icons/require-icons.js`
- `@/components/MarkdownEditor/default-options.js`
## Views
Under the `views` file, the `.vue` files representing the routes path all use `kebab-case`, and the same rules are used for folders.
Example:
- `@/views/svg-icons/index.vue`
- `@/views/svg-icons/require-icons.js`
The use of a kebab-case to name `views` is mainly due to the following considerations.
- `kebab-case` is also one of the officially recommended naming conventions [Document](https://vuejs.org/v2/style-guide/index.html#Single-file-component-filename-casing-strongly-recommended)
- The `.vue` file under `views` represents a route, so it needs to be distinguished from `component` (components are `PascalCase`)
- The `url` of the page is follow `kebab-case` , such as `https://www.xxx.admin/export-excel`. So the `view` corresponding to the route should be kept uniform.
- No case sensitive issues

108
docs/guide/advanced/theme.md Executable file
View File

@ -0,0 +1,108 @@
# Theme
This project is based on the element-ui default visual style. If you have additional requirements for visual style, you can follow the official custom theme [guide](http://element.eleme.io/#/en-US/component/custom-theme). The method is implemented by covering style variables.
## Style override
The generic style variables for element-ui may not satisfy all custom requirements, and you can do this by overriding the default component style.Since the element-ui style is introduced globally, you can't add scoped if you want to override its style in a `view`, but if you want to override only the element style of the page, you can use it. Add a class to the parent to use the namespace to solve the problem.
Or use [Deep Selectors](https://vue-loader.vuejs.org/guide/scoped-css.html#deep-selectors)。
```css
/* Your namespace */
.article-page {
/* element-ui tag */
.el-tag {
margin-right: 0px;
}
}
```
Some global element-ui style modifications can be set in [@/styles/element-ui.scss](https://github.com/adempiere/adempiere-vue/blob/master/src/styles/element-ui.scss).
<br/>
## Dynamic theme
This project provides two kinds of dynamic skinning functions, each has its own advantages and disadvantages. Please choose according to your own needs.
### Element-ui official method
After the element-ui is upgraded to 2.0, the dynamic peel function is provided in the upper right corner of the official document. This project also provides a change function.
Code: [@/components/ThemePicker](https://github.com/adempiere/adempiere-vue/blob/master/src/components/ThemePicker/index.vue)。
**Briefly explain its principle:** All styles after element-ui version 2.0 are based on SCSS, all colors are set based on a few basic color [variables](https://github.com/adempiere/custom-element-theme/blob/master/element-variables.scss), so it is not difficult to achieve dynamic skinning, as long as find a few color variables to modify it. First, we need to get the version number of element-ui through `package.json` and request the corresponding style according to the version number. After you get the style, you will change the color, replace it with the color variable you want, and then dynamically add the `style` tag to override the original CSS style.
::: tip
It is necessary to obtain the version of element-ui to lock the version so as to avoid the impact of non-compatible updates when the Element is upgraded in the future.
:::
```js
const version = require('element-ui/package.json').version
const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`
this.getCSSString(url, chalkHandler, 'chalk')
getCSSString(url, callback, variable) {
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && xhr.status === 200) {
this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, '')
callback()
}
}
xhr.open('GET', url)
xhr.send()
}
```
**How to use**
Import the ThemePicker component to your project
```js
import ThemePicker from '@/components/ThemePicker'
```
- Advantage
- No need to prepare multiple sets of themes, free dynamic theme
- Shortcomings
- Not enough customization, only support switching of basic colors
<br/>
<br/>
### Multiple sets of theme
This method is the most common way of theme, storing multiple sets of themes locally, both with different namespaces, such as writing two sets of themes, a set called `day-theme`, a set called `night-theme`, and `night-theme.` Themes are all under a `.night-theme` namespace, and we dynamically add `.night-theme` on body; remove `.night-theme`.
#### How to use
> We have made corresponding changes here based on the official theme generation library [element-theme](https://github.com/ElementUI/element-theme).
First download [custom-element-theme](https://github.com/adempiere/custom-element-theme)
```bash
git@github.com:PanJiaChen/custom-element-theme.git
```
Globally installed theme generation tool
```bash
npm i element-theme -g
```
Enter the project directory Install dependencies
```bash
npm install
```
First execute `et -i` to generate `element-variables.scss` file that stores style variables, then enter `element-variables.scss` file to modify your own variables, execute `et` after modification, compile subject, and finally Execute `gulp` to generate a namespace. All generated files are in the `dist` directory. You just copy all the contents of the file to `src/assets/custom-theme` in the `adempiere-vue` project.
::: tip
If you need to modify the name of the package generation style namespace, just modify the [variable](https://github.com/adempiere/custom-element-theme/blob/master/gulpfile.js#L6).
:::
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/0726b472-90f4-4fe9-a665-26fb8f9795c3.gif)

View File

@ -0,0 +1,11 @@
## Webpack Guide
Recommended [survivejs](https://survivejs.com/webpack/foreword/)
Here to share the webpack trilogy, basically read the webpack configuration engineer in this field.
**No English translation**
- [Getting started with webpack 4 and single page applications](https://github.com/wallstreetcn/webpack-and-spa-guide)
- [Hands touch your hand, use you with a reasonable posture using webpack4 (on)](https://juejin.im/post/5b56909a518825195f499806)
- [Hands touch your hand, use you with a reasonable posture using webpack4 (below)](https://juejin.im/post/5b5d6d6f6fb9a04fea58aabc)

220
docs/guide/essentials/deploy.md Executable file
View File

@ -0,0 +1,220 @@
# Build & Deploy
## Build
When projects are completed, you can build your application only run one command:
```bash
# build for production environment
npm run build:prod
# build for stage environment
npm run build:stage
```
After the build package is successful, the `dist` folder will be generated in the root directory, which is to build a packaged file, usually static files such as `***.js`, `***.css`, `index.html`, etc. .
If you need a custom build, such as specifying the dist directory, you need to configure it through `outputDir` in [config](https://github.com/adempiere/adempiere-vue/blob/master/vue.config.js).
### Environmental variables
The configuration of all test environments or formal environment variables is in the `.env.xxxx` file such as [.env.development](https://github.com/adempiere/adempiere-vue/blob/master/.env.development).
They all inject into the global context via the `webpack.DefinePlugin` plug-ins.
::: tip note! ! !
Environment variables must start with `VUE_APP_`. Such as: `VUE_APP_API`, `VUE_APP_TITLE`
You can access them in your application code:
```js
console.log(process.env.VUE_APP_xxxx)
```
### Analyze the build file size
If your build file is large, you can optimize your code by building and analyzing the size distribution of dependent modules using the `webpack-bundle-analyzer`.
```bash
npm run preview -- --report
```
After running you can see the specific size distribution at [http://localhost:9526/report.html](http://localhost:9526/report.html)
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/3fddf034-2b38-4299-b0d2-b748fb2abef0.jpg)
::: tip
It is recommended to use gzip, after using the volume will be only the original 1/3 or so. You can also use lazy loading or Code Splitting.
:::
## Publish
For publishing, you only have to publish the resulting static file after build, which is usually the static file in the `dist` folder, to your cdn or static server. Note that the `index.html` usually will be an entry page for your backend service. You may need to change the page's import path after determining static for JS and css.
::: tip
In deployment may find that the resource path is wrong, just modify the `@/config/index.js` file resource path.
:::
```js
// changes configure depending on your own path
publicPath: './'
```
### Router & Server
In adempiere-vue, the front-end routing uses `vue-router`, so you have two options:`browserHistory` and `hashHistory`.
Simply speaking, the difference between them is the deal with routing. `hashHistory` is processed by the path following `#`, front-end routing management through [HTML 5 History](https://developer.mozilla.org/en-US/docs/Web/API/History_API), and `browserHistory` is similar to our usual page access path, and with not `#`, but must through the server's configuration.
This project uses `hashHistory` by default, so if you have`#`in your url and you want to get rid of it, you need to switch to`browserHistory`.
Modify `src/router/index.js` mode。
```js
export default new Router({
// mode: 'history' // Need backend support
})
```
::: tip
Detail see [vue-router document](https://router.vuejs.org/zh-cn/essentials/history-mode.html)
:::
## Deploy All Ecosystem
### For all enviroment you should run the follow images:
- [ADempiere gRPC](https://hub.docker.com/r/erpya/adempiere-grpc-all-in-one)
```shell
docker pull erpya/adempiere-grpc-all-in-one
```
- [Proxy ADempiere API](https://hub.docker.com/r/erpya/proxy-adempiere-api)
```shell
docker pull erpya/proxy-adempiere-api
```
- [ADempiere Vue](https://hub.docker.com/r/erpya/adempiere-vue)
```shell
docker pull erpya/adempiere-vue
```
- [ADempiere eCommerce](https://hub.docker.com/r/erpya/adempiere-ecommerce)
```shell
docker pull erpya/adempiere-ecommerce
```
### Run Docker Stack
```yaml
# docker-compose.yaml
version: '3.7'
services:
grpc-backend:
image: erpya/adempiere-grpc-all-in-one
container_name: adempiere-backend
stdin_open: true
tty: true
environment:
- SERVER_PORT=50059
- SERVICES_ENABLED=access; business; core; dashboarding; dictionary; enrollment; log; ui; workflow; store; pos; updater;
- SERVER_LOG_LEVEL=WARNING
- DB_HOST=postgres_host
- DB_PORT=5432
- DB_NAME=adempiere
- DB_USER=adempiere
- DB_PASSWORD=adempiere
- DB_TYPE=PostgreSQL
ports:
- 50059:50059
redis:
image: redis:4-alpine
container_name: adempiere-redis
stdin_open: true
tty: true
ports:
- '6379:6379'
es7:
image: docker.elastic.co/elasticsearch/elasticsearch:7.3.2
container_name: adempiere-eslastic-search
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- ./elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml:ro
ports:
- '9200:9200'
- '9300:9300'
environment:
- discovery.type=single-node
- cluster.name=docker-cluster
- bootstrap.memory_lock=true
- ES_JAVA_OPTS=-Xmx512m -Xms512m
api-rest:
image: erpya/proxy-adempiere-api
container_name: adempiere-proxy
depends_on:
- es7
- redis
stdin_open: true
tty: true
environment:
- SERVER_PORT=8085
- AD_DEFAULT_HOST=adempiere-backend
- AD_DEFAULT_PORT=50059
- ES_HOST=adempiere-eslastic-search
- ES_PORT=9200
- VS_ENV=dev
- INDEX=vue_storefront_catalog
- RESTORE_DB=N
ports:
- 8085:8085
vue-app:
image: erpya/adempiere-vue
container_name: adempiere-frontend
stdin_open: true
tty: true
environment:
- API_URL=http://adempiere-proxy:8085
ports:
- 9526:80
e-commerce:
image: erpya/adempiere-ecommerce
container_name: adempiere-ecommerce
stdin_open: true
tty: true
environment:
- SERVER_PORT=3000
- API_URL=http://adempiere-proxy:8085
- STORE_INDEX=vue_storefront_catalog
- VS_ENV=dev
ports:
- 3000:3000
```
Note: Eslastic Search container requires a config file `elasticsearch.yaml`.
```shell
# requires superuser permissions of the operating system ('su' or 'sudo')
docker-compose up
```
Containers Running:
- adempiere-backend
- adempiere-redis
- adempiere-eslastic-search
- adempiere-proxy
- adempiere-frontend
- adempiere-ecommerce

View File

@ -0,0 +1,41 @@
## Environment Variables
`adempiere-vue` 4.0+ is built o `vue-cli`, so all environment variables are controlled based on `vue-cli`.
[Official document](https://cli.vuejs.org/guide/mode-and-env.html)
```
.env # loaded in all cases
.env.[mode] # only loaded in specified mode
```
An env file simply contains key=value pairs of environment variables:
```
FOO=bar
VUE_APP_SECRET=secret
```
::: tip note! ! !
Environment variables must start with `VUE_APP_`. Such as: `VUE_APP_API`, `VUE_APP_TITLE`
You can access them in your application code:
```js
console.log(process.env.VUE_APP_xxxx)
```
:::
In addition to `VUE_APP_*` variables, there are also two special variables that will always be available in your app code:
- `NODE_ENV` - this will be one of "development"、"production" or "test" depending on the mode the app is running in.
- `BASE_URL` - this corresponds to the `publicPath` option in `vue.config.js` and is the base path your app is deployed at.
### Build related
In addition to some environment variables written in `.env`, there are some build and deployment related variables that need to be configured in `vue.config.js`.
You can set the different parameters by executing the judgment environment with `process.env.NODE_ENV`.
Specific code can learn from [vue.config.js](https://github.com/adempiere/adempiere-vue/blob/master/vue.config.js)

63
docs/guide/essentials/import.md Executable file
View File

@ -0,0 +1,63 @@
# Import Third-party Modules
In addition to the element-ui components and the business components built into the scaffolding, sometimes we also need to import other external components.
Here to import [vue-count-to](https://github.com/adempiere/vue-countTo) as an example to introduce.
## Install dependence
Enter the following command in the terminal to complete the installation:
```bash
$ npm install vue-count-to --save
```
> add `--save` will automatically add dependencies to package.json.
<br/>
## Usage
### Global Registration
**main.js**
```js
import countTo from 'vue-count-to'
Vue.component('countTo', countTo)
```
```html
<template>
<countTo :startVal='startVal' :endVal='endVal' :duration='3000'></countTo>
</template>
```
### Local Registration
```html
<template>
<countTo :startVal='startVal' :endVal='endVal' :duration='3000'></countTo>
</template>
<script>
import countTo from 'vue-count-to';
export default {
components: { countTo },
data () {
return {
startVal: 0,
endVal: 2017
}
}
}
</script>
```
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/8b95fac0-6691-4ad6-ba6c-e5d84527da06.gif)
<br/>
## Use Any Javascript Library With Vue.js
[Use Any Javascript Library With Vue.js](https://vuejsdevelopers.com/2017/04/22/vue-js-libraries-plugins/)

115
docs/guide/essentials/layout.md Executable file
View File

@ -0,0 +1,115 @@
# Layout
The overall layout of the page is the outermost frame structure of a product and often includes navigation, sidebars, breadcrumbs, and content. To understand a admin project, first understand its basic layout.
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/7066d74f-12c5-47d6-b6ad-f22b43fec917.png)
::: tip Code
[@/layout](https://github.com/adempiere/adempiere-vue/tree/master/src/layout)
:::
`@` is webpack's [alias](https://webpack.js.org/configuration/resolve/#resolve-alias). If don't understand please study it yourself.
<br>
Most of the pages in `adempiere-vue` are based on this `layout`, except that individual pages such as: `login` , `404`, `401` , etc., do not use this layout. It is also easy if you want to have multiple layouts in a project, as long as you choose different layout component in the first-level routing.
```js
// No layout
{
path: '/401',
component: () => import('errorPage/401')
}
// Has layout
{
path: '/documentation',
// You can choose different layout components
component: Layout,
// Here the route is displayed in app-main
children: [{
path: 'index',
component: () => import('documentation/index'),
name: 'documentation'
}]
}
```
This uses vue-router [routing nesting](https://router.vuejs.org/guide/essentials/nested-routes.html), so in general, adding or modifying a page will only affect the main body of app-main. Other content in the layout, such as: the sidebar or navigation bar will not change with your main page.
```
/foo /bar
+------------------+ +-----------------+
| layout | | layout |
| +--------------+ | | +-------------+ |
| | foo.vue | | +------------> | | bar.vue | |
| | | | | | | |
| +--------------+ | | +-------------+ |
+------------------+ +-----------------+
```
<br>
## app-main
::: tip Code
[@/layout/components/AppMain](https://github.com/adempiere/adempiere-vue/blob/master/src/layout/components/AppMain.vue)
:::
Here is a layer of `keep-alive` outside the `app-main` is mainly to cache `<router-view>`, with the `tabs-view` tab navigation of the page, if you do not need to [remove](tags-view.md) it.
The `transition` defines the switching animation between pages, you can modify the transition animation according to your own needs. Related [documentation](https://vuejs.org/v2/guide/transitions.html).
Two transition animations of `fade` and `fade-transform` are provided by default. For specific css implementation, see [transition.scss](https://github.com/adempiere/adempiere-vue/blob/master/src /styles/transition.scss). If you need to adjust, you can adjust the `name` of `transition` in [AppMain.vue](https://github.com/adempiere/adempiere-vue/blob/master/src/layout/components/AppMain.vue).
<br>
## router-view
**Different router the same component vue** In a real work, there are many situations. such as:
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/ac5047c9-cb75-4415-89e3-9386c42f3ef9.jpeg)
The same component is used to create pages and edit pages. By default, when these two pages are switched, it will not trigger the created or mounted hooks of vue. [Officials say](https://router.vuejs.org/guide/advanced/data-fetching.html#data-fetching) that you can do this through the change of watch `$route`. To tell the truth it's still very troublesome. Later I discovered that I could simply add a unique key to the router-view to ensure that the routing hooks are re-rendered when the route is switched. This is much simpler.
```js
<router-view :key="key"></router-view>
computed: {
key() {
// just make sure the key is the unique
return this.$route.fullPath
}
}
```
::: tip
**Or** You can declare two different views like the `editForm` and `createForm` in this project but introduce the same component.
Code: [@/views/example](https://github.com/adempiere/adempiere-vue/tree/master/src/views/example)
:::
```html
<!-- create.vue -->
<template>
<article-detail :is-edit='false'></article-detail> //create
</template>
<script>
import ArticleDetail from './components/ArticleDetail'
</script>
<!-- edit.vue -->
<template>
<article-detail :is-edit='true'></article-detail> //edit
</template>
<script>
import ArticleDetail from './components/ArticleDetail'
</script>
```
## Mobile
The `element-ui` official position is the desktop-side framework, and most of admin project is complex, it is impossible to meet the desktop-side and mobile-side interactions through simple adaptation. Therefore, the interaction between the two ends must be different. it is recommended to re-do a system for mobile.
So, this project will not adapt to the mobile. It just does a simple response and you can modify it yourself.

202
docs/guide/essentials/mock-api.md Executable file
View File

@ -0,0 +1,202 @@
# Mock Data
Mock data is an integral part of the front-end development, the key link to separate the front and back-end development. By pre-agreed with the server-side interface, analog request data and even logic, can make the front-end development independent, will not be blocked by the development of the server.
## Swagger
In my company project, the data is usually simulated by the backend using [swagger](https://swagger.io/).
**swagger** is a REST APIs document generation tool that automatically generates documentation from code comments. It can be cross-platform, open source, supports most languages, community is good, in short, very good, highly recommended.
[Online Demo](http://petstore.swagger.io/?_ga=2.222649619.983598878.1509960455-2044209180.1509960455#/pet/addPet)
## Easy-mock
[vue-admin-template](https://github.com/adempiere/vue-admin-template) previously used [easy-mock](https://easy-mock.com/login) to simulate data.
It is a pure front-end visualization and can quickly generate persistence services for analog data. Very easy to use and can also be combined with `swagger`, support for cross-domain, whether it is a team or a personal project is worth a try.
[Online Demo](https://easy-mock.com/)
::: warning
The online version of `vue-admin-template` is no longer using `easy-mock`. Because the online free service provided by `easy-mock` is very unstable, it will hang from time to time. If you need it, you can build your own service according to its tutorial.
:::
## Mockjs
Since [adempiere-vue](https://github.com/adempiere/adempiere-vue) is a pure front-end personal project, all data is [mockjs] (https://github.com/ Nuysoft/Mock) Simulation generation. Its principle is: Intercept all requests and proxy to the local, and then mock data, so you will find that no requests are issued in `network`.
But its biggest problem is its implementation mechanism. It overrides the browser's `XMLHttpRequest` object to intercept all requests and proxy to the local. In most cases it is quite convenient to use, but because it rewrites the `XMLHttpRequest` object, so for example, the `progress` method, or some third-party libraries that rely on `XMLHttpRequest` will be incompatible with it. Looking at my project's [issues](https://github.com/adempiere/adempiere-vue/issues?utf8=%E2%9C%93&q=mock), you will know how many people have problems.
It also has a problem because it is data that is simulated locally and does not actually take any network requests. Therefore, local debugging is very troublesome and can only be debugged by `console.log`. Take the example of `adempiere-vue`. If you want to find out what data is returned by the `getInfo()` api, you can only know it by looking at the source code or manually `Debug`.
## New way <Badge text="v4.0.0+"/>
After the `v4.0` version, a `mock-server` will be launched locally to simulate the data, and the online environment will continue to use `mockjs` for simulation.(Because this project is a pure front-end project, you can also build an online server to provide data.)
The advantage of this way is to solve the previous pain points while preserving the advantages of `mockjs`. Since our mock is implemented entirely based on `webpack-dev-serve`, `mock-server` will start automatically when you start the project, and it will also pass [chokidar](https://github.com/paulmillr/chokidar) to observe the changes in the contents of the `mock` folder. When a change occurs, the previously registered `mock-api` interface is cleared and the new interface is dynamically remounted to support hot updates. If you are interested, you can look at the code [mock-server.js](https://github.com/adempiere/adempiere-vue/blob/master/mock/mock-server.js). Since it is a real `server`, you can clearly know the data structure returned by the interface through `network` of Chrome. At the same time, it solves the problem that the previous `mockjs` will rewrite the `XMLHttpRequest` object, which causes many third-party libraries to fail.
All requests for this project are sent via the packaged [request.js](https://github.com/adempiere/adempiere-vue/blob/master/src/utils/request.js) by reading The source code can find that all requests are set to a `baseURL`, and this `baseURL` is dynamically set by reading the `process.env.VUE_APP_BASE_API` environment variable, so that we can use different environments for different environments. Api` address
## Remove
If you don't want to use `mock-server`, just the `after` Middleware of `webpack-dev-server` from [vue.config.js](https://github.com/adempiere/adempiere-vue/blob/master/vue.config.js).
By default, local requests are proxy to `http://localhost:${port}/mock`, and you can modify `proxy` if you want to adjust to your own mock address.
```js
proxy: {
// change xxx-api/login => mock/login
// detail: https://cli.vuejs.org/config/#devserver-proxy
[process.env.VUE_APP_BASE_API]: {
target: `http://localhost:${port}/mock`,
changeOrigin: true,
pathRewrite: {
['^' + process.env.VUE_APP_BASE_API]: ''
}
}
},
after: require('./mock/mock-server.js')
```
**Please note: this operation requires a restart of the server.**
## Add
If you want to add mock data, just find the `mock` file in the root folder, add the corresponding route, intercept it and simulate the data.
For example, I need to add an api to get the number of comments below an article in [src/api/article](https://github.com/adempiere/adempiere-vue/blob/master/src/api/article.js) through `fetchComments`. First create a new api:
```js
export function fetchComments(id) {
return request({
url: `/article/${id}/comments`,
method: 'get'
})
}
```
After declaring the api, we need to find the corresponding mock folder [mock/article.js](https://github.com/adempiere/adempiere-vue/blob/master/mock/article.js), below Create a mock api that intercepts routes.
**Please note that the mock interception is based on routing. Please make sure that the mock data path will match your api route path(support regular)**
```js
// fetchComments 的 mock
{
// uUrl must match your api route
// For example, the route of fetchComments may be /article/1/comments or /article/2/comments
// So you need to match by regular
url: '/article/[A-Za-z0-9]/comments',
type: 'get', // Must be the same type as your interface defines
response: (req, res) => {
// return result
// req and res detail see
// https://expressjs.com/zh-cn/api.html#req
return {
code: 20000,
data: {
status: 'success'
}
}
}
}
```
## Change
The most common operation is: You have simulated some data locally, and after the backend completes the api, it gradually replaces the api of the original mock.
Let's take the `getRoles` api in [src/api/role.js](https://github.com/adempiere/adempiere-vue/blob/master/src/api/role.js) as an example. It was originally mocked in [mock/role/index.js](https://github.com/adempiere/adempiere-vue/blob/master/mock/role/index.js). Now we need to switch it to real backend data, as long as it is in [mock/role/index.js](https://github.com/adempiere/adempiere-vue/blob/master/mock/role/index.js) Find the corresponding route, then delete it. At this time you can view the real data in `network`.
```js
// The declared in the api
export function getRoles() {
return request({
url: '/roles',
method: 'get'
})
}
// Find the corresponding route and delete
{
url: '/roles',
type: 'get',
response: _ => {
return {
code: 20000,
data: roles
}
}
},
```
## Multiple servers
Currently the project only starts a `mock-server`, of course you can also have your own other `mock-server` or proxy interface. Some api can take this service, others can take another service. Just set them to a different `baseURL`. [@/utils/request.js](https://github.com/adempiere/adempiere-vue/blob/master/src/utils/request.js)
Then configure multiple `proxy` according to the set url rules in [vue.config.js](https://github.com/adempiere/adempiere-vue/blob/master/vue.config.js).
[相关文档](https://webpack.docschina.org/configuration/dev-server/#devserver-proxy)
## Enable pure front end Mock
Now in [mock/index.js](https://github.com/adempiere/adempiere-vue/blob/master/mock/index.js#L19) also encapsulates a pure front-end mock method, you only Need to be in [src/main.js](https://github.com/adempiere/adempiere-vue/tree/master/src):
```js
import { mockXHR } from '../mock'
mockXHR()
```
This will become pure front-end mock data and the same as the mock way before the `v4.0` version, the principle is as above. The online [demo](https://adempiere.github.io/adempiere-vue) that you are currently seeing is just that way.
## Switch local and online Mock data
There are many times when we encounter local use of mock data, online environments that use real data, or different environments that use different data.
- **Easy-Mock**
You need to ensure that your local simulated api is consistent with all other addresses except the root path. such as:
```
https://api-dev/login // Local request
https://api-prod/login // Online request
```
We can use the [environment variables](/guide/essentials/deploy.html#environmental-variables) to do different environments and request different api base path.
```bash
# .env.development
VUE_APP_BASE_API = '/dev-api' #Inject the root path of the local api
```
```bash
# .env.production
VUE_APP_BASE_API = '/prod-api' #Inject the root path of the production api
```
Then create an `axios` instance based on the environment variable to have a different `baseURL`.
[@/utils/request.js](https://github.com/adempiere/adempiere-vue/blob/master/src/utils/request.js)
```js
// create an axios instance
const service = axios.create({
baseURL: process.env.BASE_API, // base_url of the API
timeout: 5000 // request timeout
})
```
In this way we can automatically switched local and online apis based on environment variables.
- **Mock.js**
When we use `Mock.js` to simulate data locally, the real-world api method is used online. This is similar to the easy-mock method above. We mainly judge that when it is an online environment, we use real-world api. We only import `Mock.js` locally.
```js
// main.js
// use environment variables to determine is required
if (process.env.NODE_ENV === 'development') {
require('./mock') // simulation data
}
```
Mock data is only import in the local environment.

144
docs/guide/essentials/new-page.md Executable file
View File

@ -0,0 +1,144 @@
# New Page
If you are familiar with the `vue-router` then it will be very simple.
First add the route to the `@/router/index.js`.
**Such as: add an excel page**
```js
{
path: '/excel',
component: Layout,
redirect: '/excel/export-excel',
name: 'excel',
meta: {
title: 'excel',
icon: 'excel'
}
}
```
::: tip
It just creates a blank route based on 'layout', and you also need to add a route to the 'children' below it.
:::
```js
{
path: '/excel',
component: Layout,
redirect: '/excel/export-excel',
name: 'excel',
meta: {
title: 'excel',
icon: 'excel'
},
children: [
{
path: 'export-excel',
component: ()=>import('excel/exportExcel'),
name: 'exportExcel',
meta: { title: 'exportExcel' }
}
]
}
```
**This sidebar will appear the menu-item**
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/2ab6921d-f9bb-4fbb-a151-0e6027e23a6e.png)
<br/>
:::tip
Since `children` only declares one route below, there will be no expansion arrow. If the number of routes under `children` is greater than 1, there will be an expansion arrow, as shown below.
If you want to ignore this automatic decision, you can use `alwaysShow: true`, so that it will ignore the previously defined rule and display the root route. See the [Router and Nav](router-and-nav.md) for details.
:::
```js
{
path: '/excel',
component: Layout,
redirect: '/excel/export-excel',
name: 'excel',
meta: {
title: 'excel',
icon: 'excel'
},
children: [
{ path: 'export-excel', component:()=>import('excel/exportExcel'), name: 'exportExcel', meta: { title: 'exportExcel' }},
{ path: 'export-selected-excel', component:()=>import('excel/selectExcel'), name: 'selectExcel', meta: { title: 'selectExcel' }},
{ path: 'upload-excel', component:()=>import('excel/uploadExcel'), name: 'uploadExcel', meta: { title: 'uploadExcel' }}
]
}
```
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/89d6a0b8-5cf7-4a19-9afd-7267ec454066.png)
**The sidebar will appear the `submenu`.**
<br/>
## Nested Routes
If you have a nested Route, such as [@/views/nested](https://github.com/adempiere/adempiere-vue/tree/master/src/views/nested),
**Don't forget to manually add an `< router-view >` to the root file of the secondary directory**.
```html
<!-- parent view -->
<template>
<div>
<!-- xxx html dom -->
<router-view />
</div>
</template>
```
Such as: [@/views/nested/menu1/index.vue](https://github.com/adempiere/adempiere-vue/blob/master/src/views/nested/menu1/index.vue).
**Note:** As many `<router-view>` as the level of routes nested.
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/9459de62-64d0-4819-9730-daf3f9889018.png)
<br/>
## Create View
After adding the route, create a view under the `@/views`. As usual, a router correspond
a view.
Suggestion if a component or utils function only used in this view, just create a component folder under this view, lt is more convenient for each module to maintain its own `utils` or `components`.
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/8ca55a30-c22c-4143-aa8d-2a0d3e04fc33.png)
<br/>
## Create Api
Finally, under the [@/api](https://github.com/adempiere/adempiere-vue/tree/master/src/api) folder, create the corresponding api service for this module.
## Create Component
Personally write vue project habits, the global `@/components` will only write some global components, such as rich text, various search components, packaged date components, etc. can be shared components. Each page or module-specific business component is written under the current views. Such as: `@/views/article/components/xxx.vue`. This split greatly reduces maintenance costs.
**Remember that the biggest benefit of splitting components is not shared code but maintainability! **
## Create Style
The page's style and components are the same. The global `@/style` writes a global common style. The style of each page is written under the current `views`. Please remember to add `scoped` or namespace to avoid Causes global style pollution.
```css
<style>
/* global styles */
</style>
<style scoped>
/* local styles */
.xxx-container{
/* name scoped */
xxx
}
</style>
```

View File

@ -0,0 +1,79 @@
# Permission
It has been introduced in detail in this article--[手摸手,带你用 vue 撸后台 系列二(登录权限篇)](https://juejin.im/post/591aa14f570c35006961acac).
The implementation of this project's permission is: compare the routing table by obtaining the current user's permission, and generate the routing table accessible by the current user with the permission, and dynamically mount it to `router` through `router.addRoutes`.
But in fact, the business logic of many companies may not be the case. For example, the requirement of many companies is that the permissions of each page are dynamically configured, unlike the default settings in this project. But in fact the principle is the same. For example, you can dynamically configure permissions for each page through a tree control or other presentation, and then store this routing table to the back end. When the user logs in to get `roles`, the front end requests the accessible routing table to the backend according to `roles`, so that the accessible pages are dynamically generated. After that, the router.addRoutes is dynamically mounted to the router. You will find the same. , never change their case.
Just one more step to map the back-end return routing table with the local components. [issue](https://github.com/adempiere/adempiere-vue/issues/293)
```js
const map={
login:require('login/index').default // sync
login:()=>import('login/index') // async
}
// The map on which you have a server is similar with
const serviceMap=[
{ path: '/login', component: 'login', hidden: true }
]
// After traversing this map, dynamically generate asyncRoutes
And replace component with map[component]
```
Ps: Do not rule out this project will increase the permissions control panel to support true dynamic configuration permissions.
## Logical modification
The control code of the routing level right now is in `@/permission.js`. If you want to change the logic, you can release the hook `next()` directly in the appropriate judgment logic.
## Permission directive
Write a permission directive, and can easily and quickly implement button-level permission judgment. [v-permission](https://github.com/adempiere/adempiere-vue/tree/master/src/directive/permission)
**Use**
```html
<template>
<!-- Admin can see this -->
<el-tag v-permission="['admin']">admin</el-tag>
<!-- Editor can see this -->
<el-tag v-permission="['editor']">editor</el-tag>
<!-- Editor can see this -->
<el-tag v-permission="['admin','editor']">Both admin or editor can see this</el-tag>
</template>
<script>
// Of course you can also register it for the sake of convenience.
import permission from '@/directive/permission/index.js'
export default{
directives: { permission }
}
</script>
```
**Limitations**
In some cases it is not suitable to use v-permission, such as element Tab component which can only be achieved by manually setting the v-if.
You can use the global permission judgment function. The usage is similar to the instruction `v-permission`.
```html
<template>
<el-tab-pane v-if="checkPermission(['admin'])" label="Admin">Admin can see this</el-tab-pane>
<el-tab-pane v-if="checkPermission(['editor'])" label="Editor">Editor can see this</el-tab-pane>
<el-tab-pane v-if="checkPermission(['admin','editor'])" label="Admin-OR-Editor">Both admin or editor can see this</el-tab-pane>
</template>
<script>
import checkPermission from '@/utils/permission'
export default{
methods: {
checkPermission
}
}
</script>
```

View File

@ -0,0 +1,310 @@
# Router and Nav
Router and Nav are the key skeleton for organizing a management system.
This project router and nav are bound together, so you only have to configure the route under `@/router/index.js` and the sidebar nav will be dynamically generated automatically. This greatly reduces the workload of manually editing the sidebar nav. Of course, so you need to follow many conventions in configuring the route.
## Config
First let us know what configuration items are provided config route.
```js
// if set to true, lt will not appear in sidebar nav.
// e.g. login or 401 page or as some editing pages /edit/1 (Default: false)
hidden: true
// this route cannot be clicked in breadcrumb navigation when noRedirect is set
redirect: noRedirect
// when you route a children below the declaration of more than one route,
// it will automatically become a nested mode - such as the component page
// when there is only one, the child route will be displayed as the root route
// if you want to display your root route
// regardless of the number of children declarations under the route
// you can set alwaysShow: true
// so that it will ignore the previously defined rules and always show the root route
alwaysShow: true
// set router name. It must be setin order to avoid problems with <keep-alive>.
name: 'router-name'
meta: {
// required roles to navigate to this route. Support multiple permissions stacking.
// if not set means it doesn't need any permission.
roles: ['admin', 'editor']
// the title of the route to show in various components (e.g. sidebar, breadcrumbs).
title: 'title'
// svg icon class
icon: 'svg-name' // or el-icon-x
// when set true, the route will not be cached by <keep-alive> (default false)
noCache: true
// if false, the item will hidden in breadcrumb(default is true)
breadcrumb: false
// if set to true, it can be fixed in tags-view (default false)
affix: true // this is very useful in some scenarios, // click on the article to enter the article details page,
// When you set, the related item in the sidebar will be highlighted
// for example: a list page route of an article is: /article/list
// at this time the route is /article/1, but you want to highlight the route of the article list in the sidebar,
// you can set the following
activeMenu: '/article/list'
}
```
<br/>
**Example**
```js
{
path: '/permission',
component: Layout,
redirect: '/permission/index',
hidden: true,
alwaysShow: true,
meta: { roles: ['admin','editor'] }, // you can set roles in root nav
children: [{
path: 'index',
component: _import('permission/index'),
name: 'permission',
meta: {
title: 'permission',
icon: 'lock',
roles: ['admin','editor'], // or you can only set roles in sub nav
noCache: true
}
}]
}
```
## Router
There are two types of routes here , `constantRoutes` and `asyncRoutes`.
**constantRoutes:** represents routes that do not require dynamic access, such as login page, 404, general page, and so on.
**asyncRoutes:** represents pages that require dynamic judgment permissions and are dynamically added through `addRouters`. The details will be introduced on the [permission](permission.md).
::: tip
All routing pages here use the `router lazy loading`, as described in [document](/guide/advanced/lazy-loading.md)
If you want to know more about browserHistory and hashHistory, please refer to [Build & Deploy](deploy.md).
:::
The other configurations are no different from the [vue-router](https://router.vuejs.org/en/) official, so check the documentation for yourself.
::: warning
There is one thing to be careful about is that the 404 page must be the last to load, if it is declared in constantRoutes. Later declared pages will be blocked to 404, see the details of the problem: [addRoutes when you've got a wildcard route for 404s does not work](https://github.com/vuejs/vue-router/issues/1176)
:::
## Sidebar
The project sidebar is mainly based on the `el-menu` of element-ui.
Also introduced in the front, the sidebar is generated dynamically by reading the route and combined with the permission judge, but also need to support the infinite nesting of routes, so here is also used to the recursive components.
> Code: [@/layout/components/Sidebar](https://github.com/adempiere/adempiere-vue/tree/master/src/layout/components/Sidebar)
This also modify many default sidebar styles of `element-ui`. All css can be found in [@/styles/sidebar.scss](https://github.com/adempiere/adempiere-vue/blob/master/src/styles/sidebar.scss) and can be modified to suit your needs.
**Here need to pay attention**. The general sidebar has two forms, `submenu` and`el-menu-item`. One is a nested submenu, the other is a direct link. As shown below:
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/e94739d6-d701-45c8-8c6e-0f4bb10c3b46.png)
The sidebar has already helped you to make a judgment. When you route a children below the declaration of more than >1 routes, it will automatically become a nested mode. If the sub-route is exactly equal to one, the sub-route is displayed as a root route in the sidebar by default. If you do not want to, you can disable this feature by setting `alwaysShow: true` in the root route. Such as:
```js
// no submenu, because children.length===1
{
path: '/icon',
component: Layout,
children: [{
path: 'index',
component: ()=>import('svg-icons/index'),
name: 'icons',
meta: { title: 'icons', icon: 'icon'}
}]
},
// has submenu, because children.length>=1
{
path: '/components',
component: Layout,
name: 'component-demo',
meta: {
title: 'components',
icon: 'component'
},
children: [
{ path: 'tinymce', component: ()=>import('components-demo/tinymce'), name: 'tinymce-demo', meta: { title: 'tinymce' }},
{ path: 'markdown', component: ()=>import('components-demo/markdown'), name: 'markdown-demo', meta: { title: 'markdown' }},
]
}
```
::: tip unique-opened
You can set `unique-opened` in [Sidebar/index.vue](https://github.com/adempiere/adempiere-vue/blob/master/src/layout/components/Sidebar/index.vue). To control the sidebar, whether to keep only one submenu expanded.
:::
## Nested Routes
If you have a nested Route, such as [@/views/nested](https://github.com/adempiere/adempiere-vue/tree/master/src/views/nested),
**Don't forget to manually add an `< router-view >` to the root file of the secondary directory**.
```html
<!-- parent view -->
<template>
<div>
<!-- xxx html dom -->
<router-view />
</div>
</template>
```
Such as: [@/views/nested/menu1/index.vue](https://github.com/adempiere/adempiere-vue/blob/master/src/views/nested/menu1/index.vue).
**Note:** As many `<router-view>` as the level of routes nested.
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/9459de62-64d0-4819-9730-daf3f9889018.png)
<br/>
## Click the sidebar to refresh the current route
Before using the development model of spa(single page application), each time the user clicks the sidebar will request this page again, the user gradually developed the habit of clicking the current route in the sidebar to refresh the view. But now the spa is not the same, the user clicks the currently highlighted route and does not refresh the view, because the vue-router will intercept your routing, it determines your url does not change, so it will not trigger any hook or view changes.[Related issue](https://github.com/vuejs/vue-router/issues/296), the community has also heated discussions on the issue.
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/5d0b0391-ea6a-45f2-943e-aff5dbe74d12.png)
`yyx990803`also said that he wanted to add a way to brighten the view, but later he changed his mind again/(ㄒ o ㄒ)/~~ But demand is here, what should we do? He said it would not trigger anything without changing the current URL, so can I force the trigger? The hack is simple. By changing the url query to trigger the view changes。We listen to each link's click event on the sidebar, each click will push a different query for the router to ensure that the view is refreshed.
```js
clickLink(path) {
this.$router.push({
path,
query: {
//Ensure that each click, query is not the same
//to ensure that refresh the view
t: +new Date()
}
})
}
```
ps: Don't forget to add a unique `key` to `router-view`, such as `<router-view :key="$route.path"></router-view>`.
But there's also a drawback the ugly `query` suffix behind url, such as `xxx.com/article/list?t=1496832345025`
You can know from the previous issue that there are many other options. In my company project, the solution adopted is to determine whether the currently clicked menu route is consistent with the current route. However, when it is consistent, it will jump to a dedicated Redirect page, which will redirect the route to Go to the page, this will have a refresh effect.
**Example**
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/0dd7f78b-0fb5-4c7d-8236-cee78f960984.jpg)
Click on the global size switch button shown in the image and you will see that the page of `app-main` has been refreshed. It uses the method of redirecting to the `Redirect` page and then redirecting back to the original page.
Redirect page to `/redirect` when clicking
```js
const { fullPath } = this.$route
this.$router.replace({
path: '/redirect' + fullPath
})
```
The `redirect` page is redirected back to the original page
```js
// redirect.vue
// https://github.com/adempiere/adempiere-vue/blob/master/src/views/redirect/index.vue
export default {
beforeCreate() {
const { params, query } = this.$route
const { path } = params
this.$router.replace({ path: '/' + path, query })
},
render: function(h) {
return h() // avoid warning message
}
}
```
<br>
## Breadcrumb
This project also packages a breadcrumb navigation, which is also dynamically generated by the watch $route change. It is the same with the menu, you can also config it in the routing. You can also add some custom attributes to your business needs in route.meta attr. For example, you can declare `breadcrumb:false` in the route so that it is not displayed in breadcrumb.
![](https://adempiere-vue.gitee.io/gitee-cdn/adempiere-vue-site/4c60b3fc-febd-4e22-9150-724dcbd25a8e.gif)
> Corresponding code: [@/components/Breadcrumb](https://github.com/adempiere/adempiere-vue/blob/master/src/components/Breadcrumb/index.vue)
## Sidebar scroll problem
Previous versions of scroll were handled with css
```css
overflow-y: scroll;
::-webkit-scrollbar {
display: none;
}
```
But hack by css has some problems, in Firefox or other lower versions of the browser will be less beautiful.
Second, in the case of sidebar collapses, limited to `menu` of`element-ui`, can not be handled in this way.
So the current version uses `el-scrollbar` to handle the sidebar scrolling problem.
::: tip Code
[@/layout/components/Sidebar](https://github.com/adempiere/adempiere-vue/blob/master/src/layout/components/Sidebar/index.vue)
:::
## Sidebar external-link <Badge text="v3.8.2+"/>
You can also configure an external-link in the sidebar. As long as you fill in the legal url path in `path`, you will be able to open this page when you click on the sidebar.
E.g.
```json
{
"path": "external-link",
"component": Layout,
"children": [
{
"path": "https://github.com/adempiere/adempiere-vue",
"meta": { "title": "externalLink", "icon": "link" }
}
]
}
```
## Sidebar expands by default
In some scenarios, users need to expand some of the `sub-menu` in the sidebar by default, as shown below:
<img :src="$withBase('/images/default-openeds.jpg')" alt="default-openeds.jpg" width="250px">
Can be set through `default-openeds`, first find [Sidebar Code](https://github.com/adempiere/adempiere-vue/blob/master/src/layout/components/Sidebar/index.vue)
```html
<el-menu
:default-openeds="['/example','/nested']" // Add this line of code
:default-active="activeMenu"
:collapse="isCollapse"
:background-color="variables.menuBg"
:text-color="variables.menuText"
:unique-opened="false"
:active-text-color="variables.menuActiveText"
:collapse-transition="false"
mode="vertical"
>
<sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path" />
</el-menu>
```
**Note: default-openeds = "['example', 'nested']" is filled with route-path of submenu**

101
docs/guide/essentials/server.md Executable file
View File

@ -0,0 +1,101 @@
# Work with Server
## Front-end request flow
In `adempiere-vue` , a complete front-end UI interacts to the server-side processing flow as follows:
1. UI component interaction;
2. Call unified management API service request function;
3. Send requests using encapsulated request.js;
4. Get server return;
5. Update data;
As you can see from the above flow, in order to facilitate management and maintenance, unified request processing is placed in the `src/api` folder and the files are generally split according to the model latitude,such as:
```
api/
login.js
article.js
remoteSearch.js
...
```
## request.js
`@/utils/request.js` is based on the [axios](https://github.com/axios/axios), to facilitate the uniform handling of POST, GET and other request parameters, request headers, and error messages。Specific can see [request.js](https://github.com/adempiere/adempiere-vue/blob/master/src/utils/request.js).
It encapsulates the global `request interceptor`, `response interceptor`,`unified error handling`, `unified timeout, baseURL settings, etc.`
## An example of a request article list:
```js
// api/article.js
import request from '../utils/request';
export function fetchList(query) {
return request({
url: '/article/list',
method: 'get',
params: query
})
}
// views/example/list
import { fetchList } from '@/api/article'
export default {
data() {
list: null,
listLoading: true
},
methods: {
fetchData() {
this.listLoading = true
fetchList().then(response => {
this.list = response.data.items
this.listLoading = false
})
}
}
}
```
## Set multiple baseURLs
We can request multiple api addresses by setting multiple `baseURL`s through [environment variables](/guide/essentials/deploy.html).
```bash
# .env.development
VUE_APP_BASE_API = '/dev-api' #Inject the root path of the api
VUE_APP_BASE_API2 = '/dev-api2' #Inject the root path of the api
```
Then create an `axios` instance based on the environment variable, giving it a different `baseURL` [@/utils/request.js](https://github.com/adempiere/adempiere-vue/blob/master/src/utils/request.js)
```js
// create an axios instance
const service = axios.create({
baseURL: process.env.BASE_API, // api base_url
timeout: 5000 // request timeout
})
const service2 = axios.create({
baseURL: process.env.BASE_API2, // api base_url
timeout: 5000 // request timeout
})
```
Or
```js
export function fetchList(query) {
return request({
url: '/article/list',
method: 'get',
params: query,
baseURL: 'xxxx' // direct coverage
})
}
```
## Switch from mock directly to server request
See [Mock Data](mock-api.md)

147
docs/guide/essentials/style.md Executable file
View File

@ -0,0 +1,147 @@
# Style
## CSS Modules
In the code of stylIn the style development process, there are two issues are more prominent:
- Global pollution —— The selector in the CSS file is global. The same name selector in different files, according to the order in the build generation file, the styles generated later will overwrite the previous ones.
- Selector complex —— In order to avoid the above problems, we have to be careful when writing styles, the name of the class will be marked with a range of restrictions, multi-person development is also very easy to lead to the chaos of the naming style. The classnames getting longer and longer. Eventually, it's hard to maintain.
Fortunately vue provides us with [scoped](https://vue-loader.vuejs.org/guide/scoped-css.html#mixing-local-and-global-styles) can easily solve the above problem. As the name suggests, it adds a scoped concept to css.
```css
/* Compile before */
.example {
color: red;
}
/* Compile after */
.example[_v-f3f3eg9] {
color: red;
}
```
If you add `<style scoped>` the css will only effect in the current component。For detailed documentation, see [vue-loader](https://vue-loader.vuejs.org/guide/scoped-css.html#mixing-local-and-global-styles)
::: tip
With scoped, the parent component's styles will not leak into child components. However, a child component's root node will be affected by both the parent's scoped CSS and the child's scoped CSS. This is by design so that the parent can style the child root element for layout purposes.
:::
<br/>
## Project Structure
adempiere-vue All global styles are set in the `@/styles` directory.
```bash
├── styles
│ ├── btn.scss # button css
│ ├── element-ui.scss # global custom element-ui style
│ ├── index.scss # global common style
│ ├── mixin.scss # global sass mixin
│ ├── sidebar.scss # sidebar css
│ ├── transition.scss # vue transition animation
│ └── variables.scss # global variables
```
The common workflow is that the global styles are written in the `src/styles` directory and each page's own style is written in its own `.vue` file.
```css
<style>
/* global styles */
</style>
<style scoped>
/* local styles */
</style>
```
## Custom element-ui style
Now let's talk about how to override the element-ui style. Because element-ui style we are import in the global, so you can't add `scoped` to a page if you want to overwrite it, but you want to override only the element style of this page, you can add a class in its parent, using the namespace to solve this problem.
```css
.article-page {
/* you namespace*/
.el-tag {
/* element-ui element tag*/
margin-right: 0px;
}
}
```
**Of course, you can also use the deep selectors as described below.**
## Deep Selectors
**Parent component changes child component style.**
If you want a selector in scoped styles to be "deep", i.e. affecting child components, you can use the >>> combinator:
```css
<style scoped>
.a >>> .b { /* ... */ }
</style>
```
Will be compiled into
```css
.a[data-v-f3f3eg9] .b {
/* ... */
}
```
Some pre-processors, such as SASS, may not be able to parse >>> properly. In those cases you can use the /deep/ combinator instead - it's an alias for >>> and works exactly the same.
```css
.xxx-container >>> .el-button{
xxxx
}
```
[Official document](https://vue-loader.vuejs.org/en/features/scoped-css.html)
## Postcss
Let's talk about the configuration of postcss. After the new version of the [vue-cli webpack template](https://github.com/vuejs-templates/webpack) initialization, there is a default `postcss.config.js` in the root directory. By default, `vue-loader` will read the configuration of postcss from it, so here directly to change the configuration file on it. The configuration is the same as [postcss](https://github.com/postcss/postcss).
```javascript
// postcss.config.js
module.exports = {
plugins: {
autoprefixer: {}
}
}
// package.json
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
```
As described in the previous code, autoprefixer reads the configuration parameters of browserslist under package.json.
- `> 1%` Compatible with browser with global usage above 1%
- `last 2 versions` Compatible with the last two versions of each browser
- `not ie <= 8` Not compatible ie8 and below
More detail [browserslist](https://github.com/ai/browserslist)
`postcss` has many other features [to explore by yourself](https://www.postcss.parts/)
## Mixin
This project does not set to automatically inject sass mixin to the global, so you need to manually introduce the mixin.
```scss
<style rel="stylesheet/scss" lang="scss">
@import "src/styles/mixin.scss";
</style>
```
If you need to automatically inject mixin global, you can use
[sass-resources-loader](https://github.com/shakacode/sass-resources-loader).

View File

@ -0,0 +1,113 @@
# Tags View
This feature is to respond to people's needs. In fact, I do not use this feature in company projects or personal projects. In the past, those traditional back-end frameworks often included this feature. Since most of the previous back-end projects were in the form of multiple pages, the navigation feature of the tags view still has some basic meaning. Most of them are based on the iframe.
However, with the development of the times, the background projects are almost all spa (single page web application single page development), and it is obviously not appropriate to use the previous way to implement the navigation of the tags.
So the current plan is:
Use a combination of `keep-alive` and `router-view` .
Code: `@/layout/components/AppMain.vue`
```html
<keep-alive :include="cachedViews">
<router-view></router-view>
</keep-alive>
```
The actual action of the tags view navigation is equivalent to another nav display mode. In fact, it is a router-link, and click to jump to the corresponding page. Then we are listening to changes in the route `$route` to determine if the current page needs to be reloaded or cached.
## visitedViews && cachedViews
The current tag-view maintains two arrays.
- visitedViews : The page the user has visited is a collection of tag arrays displayed in the tags bar navigation.
- cachedViews : The actual keep-alive route. You can set whether or not you want to cache the route by configuring the route with `meta.noCache`.
[Configuration Document](router-and-nav.md)
## Precautions
Because keep-alive and router-view are strongly coupled, and it is not difficult to find the keep-alive include default is to match the component's name, it is necessary to look at the document and source code when writing the routing component corresponding to the routing router and route.
Make sure the name of both is exactly the same. (Keep in mind that the naming of the name is as unique as possible. Remember not to duplicate the naming of some components, or to refer to the last memory overflow issue recursively.)
**DEMO:**
```js
//Define routes
{
path: 'create-form',
component: ()=>import('@/views/form/create'),
name: 'createForm',
meta: { title: 'createForm', icon: 'table' }
}
```
```js
//The corresponding view of the route. such as: form/create
export default {
name: 'createForm'
}
```
Make sure that the two names are the same. Remember not to write duplicates or mistakes. By default, if you do not write name, it will not be cached.
For details, see
[issue](https://github.com/vuejs/vue/issues/6938#issuecomment-345728620).
## Cache is not suitable for the scene
Currently cached solutions are not suitable for certain services, such as the article details page such as `/article/1``/article/2`, their routes are different but the corresponding components are the same, so their component name is the same, As mentioned earlier, the `keep-alive` include can only be cached based on the component name, so this is a problem. There are currently two solutions:
- Instead of using keep-alive's include, keep-alive caches all components directly. This way, it supports the aforementioned business situation.
To [@/layout/components/AppMain.vue](https://github.com/adempiere/adempiere-vue/blob/master/src/layout/components/AppMain.vue) remove the `include` related code. Of course, using keep-alive directly also has disadvantages. He can't dynamically delete the cache. You can only help it to set a maximum cache instance limit.
[issue](https://github.com/vuejs/vue/issues/6509)
- Use a browser cache scheme such as localStorage, own to control the cache.
## Affix <Badge text="v3.10.0+"/>
If the Affix attribute is added to the route, the current `tag` will be fixed in `tags-view` (cannot be deleted).
![](https://user-images.githubusercontent.com/8121621/52840303-cd5c9280-3133-11e9-928f-e2825eaab51b.png)
```js {14}
{
path: '',
component: Layout,
redirect: 'dashboard',
children: [
{
path: 'dashboard',
component: () => import('@/views/dashboard/index'),
name: 'Dashboard',
meta: {
title: 'dashboard',
icon: 'dashboard',
noCache: true,
affix: true
}
}
]
}
```
## Remove
In fact, keep-alive [source code](<(https://github.com/vuejs/vue/blob/dev/src/core/components/keep-alive.js)>) is not complicated, but the logic is still quite around. Before the vue author himself fixed a bug, he was not careful, he made two versions to fix it, so if there is no user who needs the navigation bar, it is recommended Remove this feature.
First find
`@/layout/components/AppMain.vue` and remove `keep-alive`
```html
<template>
<section class="app-main" style="min-height: 100%">
<transition name="fade-transform" mode="out-in">
<router-view></router-view> <!-- or <router-view :key="key"/> -->
</transition>
</section>
</template>
```
Remove the entire file `@/layout/components/TagsView.vue`. Then, remove the reference to `TagsView` in `@/layout/components/index` and in `@/layout/Layout.vue`. Finally, remove the file `@/store/modules/tagsView`.

View File

@ -0,0 +1,11 @@
# Gitter
[Gitter](https://gitter.im/adempiere-vue/discuss)
<script>
export default {
mounted () {
window.open('https://gitter.im/adempiere-vue/discuss')
}
}
</script>

View File

@ -0,0 +1,11 @@
# Release Notes
[Release Notes](https://github.com/adempiere/adempiere-vue/releases)
<script>
export default {
mounted () {
window.open('https://github.com/adempiere/adempiere-vue/releases')
}
}
</script>

37
docs/package.json Normal file
View File

@ -0,0 +1,37 @@
{
"name": "adempiere-vue",
"version": "2.0.0",
"description": "adempiere-vue have all related to documentation of ADempiere Vue Client",
"main": "index.js",
"scripts": {
"start": "vuepress dev",
"dev": "vuepress dev",
"build": "vuepress build",
"precommit": "lint-staged",
"lint": "prettier --write '**/*.md'"
},
"lint-staged": {
"*.{js,json,css,md,vue}": [
"prettier --write",
"git add"
]
},
"prettier": {
"semi": false,
"singleQuote": true
},
"author": "PanJiaChen",
"license": "ISC",
"devDependencies": {
"prettier": "1.14.2",
"vuepress": "0.14.9",
"husky": "0.14.3",
"lint-staged": "7.2.2"
},
"dependencies": {
"axios": ">=0.21.1",
"blockadblock": "3.2.1",
"sweetalert2": "9.10.9",
"webpack-dev-middleware": "3.6.0"
}
}

38
docs/zh/README.md Executable file
View File

@ -0,0 +1,38 @@
---
home: true
heroImage: /home.png
title: a
actionText: 快速上手 →
actionLink: /zh/guide/
features:
- title: 丰富功能
details: 提炼了典型的业务模型,提供了丰富的功能组件
- title: 最佳实践
details: 合理的框架选择,良好的工程实践助你持续产出高质量代码
- title: 最新技术栈
details: 使用 vue/vuex/vue-router/element 等前端前沿技术开发
- title: 权限验证
details: 根据权限动态加载路由,渲染侧边栏
- title: 国际化
details: 内建业界通用的国际化方案
- title: 主题
details: 支持多种动态换肤功能
footer: MIT Licensed | Copyright © 2017-present PanJiaChen
---
## Getting Started
```bash
# clone the project
git clone https://github.com/adempiere/adempiere-vue.git
# install dependency
npm install
# develop
npm run dev
```
## Demo
[ADempiere UI Demo](https://demo-ui.erpya.com/)

9
docs/zh/donate/README.md Normal file
View File

@ -0,0 +1,9 @@
---
sidebar: false
---
::: tip Donate
如果你觉得这个项目帮助到了你,你可以帮作者买一杯果汁表示鼓励 :tropical_drink:
:::
[PayPal](https://www.paypal.me/YamelSenih)

Some files were not shown because too many files have changed in this diff Show More