Hélio Augusto Biancardi
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # Arquitetura - Admin 3.0 Este documento visa explicar a nova estrutura do admin para o b2b. Mudando a arquitetura do frontend para vuejs, os nomes e estruturas das pastas foram escolhidos para manter padrões de trabalho do nosso dia a dia. # Sumário - [Plugins e versões](#Plugins-e-versões) - [Vue Auto Routing](#Vue-Auto-Routing) - [Vue Router Layout](#Vue-Router-Layout) - [Estrutura da aplicação](#Estrutura-da-aplicação) - [Vue Router](#Vue-Router) - [Vuetify](#Vuetify) - [Axios](#Axios) - [Vuex](#Vuex) - [Helpers customizaveis do sistema](#Helpers-customizaveis-do-sistema) - [Uso dos componentes padrões do sistema](#Uso-dos-componentes-padrões-do-sistema) - [Uso das props globais](#Uso-das-props-globais) - [Padronização no backend do admin](#Padronização-no-backend-do-admin) - [Funcionamento do hot reload em ambiente de desenvolvimento](#Funcionamento-do-hot-reload-em-ambiente-de-desenvolvimento) - [Criação de uma nova tela](#Criação-de-uma-nova-tela) ## Plugins e versões ### Dependências - Vue `^2.6.11` - Vue-Router `^3.1.3` - Vue-Router-Layout `^0.1.2` - Vuetify `^2.2.11` - Axios `^0.19.0` - Vuex `^3.4.0` ___ ### **Dependências de desenvolvimento** - Vue-Cli-Plugin-Auto-Routing `^0.1.2` - Vue-Auto-Routing `^0.4.1` - Vuetify-Loader `^1.3.0` ## Vue Auto Routing > [Github](https://github.com/ktsn/vue-cli-plugin-auto-routing) Este plugin é usado para gerar rotas automaticamente de acordo com a estrutura das pastas. É possível escolher qual a raiz das pastas que serão transformadas em rotas. Exemplo: ``` src/ ├── layouts/ ├── pages/ * pages foi a raiz escolhida para auto routing * ├── produtos/ ├── index.vue ├── index.vue └── router.js ``` Então teremos as rotas: ``` url.com/produtos/index url.com/index ``` ## Vue Router Layout > [Github](https://github.com/ktsn/vue-router-layout) Este plugin é usado para escolher o layout de cada componente renderizado. Os layouts são definidos no diretório `src/layouts` sendo `default.vue` o layout padrão: ``` src/ ├── layouts/ ├── default.vue ├── pages/ └── router.js ``` O layout default é estruturado do seguinte modo: > default.vue ``` [vuejs] <template> <v-app id="inspire"> <side-bar/> <v-content id="container-iframe"> <v-container v-if="legacies.length > 0" fluid> <v-tabs v-model="tab" background-color="secondary" :show-arrows="true"> <v-tab v-for="(item, key) in legacies" :key="key" :href="`#tab-${key}`"> {{ item.name }} <v-tooltip top> <template v-slot:activator="{ on, attrs }"> <v-btn icon v-bind="attrs" v-on="on" @click="removeLegacyFrame(key)"> <v-icon x-small>mdi-close</v-icon> </v-btn> </template> <span>Fechar aba</span> </v-tooltip> </v-tab> <v-tab-item v-for="(item, key) in legacies" :key="key" :value="'tab-' + key" style="height: 100%;"> <v-card class="mx-auto" :height="maxContentHeight" tile> <iframe :src="item.url" frameborder="0" style="width: 100%; height: 100%;"></iframe> </v-card> </v-tab-item> </v-tabs> </v-container> <v-container class="fill-height" fluid v-else> <v-row align="center" justify="center"> <v-col class="text-center"> <router-view/> </v-col> </v-row> </v-container> </v-content> <copyFooter/> </v-app> </template> ``` Por padrão os componentes renderizados usam o layout default, porém podem ser escolhidos da seguinte forma: > produtos/gerenciar.vue ``` [vuejs] <template> <div> <h1>Produtos</h1> </div> </template> <script> export default { layout: 'layout1' * layout1 escolhido para ser o layout usado na renderização } </script> ``` Mas ao mesmo tempo que o arquivo default renderiza os componentes criado em vue js, ele foi customizado para suportar a renderização das telas legadas do admin, para que a nova estrutura seja compatível tanto com as telas antigas, quanto as novas, é possível visualizar a renderização pela parte a seguir: ``` [vuejs] <iframe :src="item.url" frameborder="0" style="width: 100%; height: 100%;"></iframe> ``` ## Estrutura da aplicação O diretório atual do projeto está definida na pasta `/front` dentro do `/admin`: ``` admin/ ├── .gitlab/ ├── .vscode/ ├── ci/ ├── docker/ ├── docs/ ├── front/ * projeto vue do admin ├── src/ ├── test/ ├── .editorconfig ├── .env ├── .gitignore ├── .gitlab-ci.yml ├── .gitlab-deploy.sh ├── build.sh ├── composer.json ├── docker-compose.yml ├── Dockerfile ├── gitextract.sh ├── install.sh ├── README.md ├── shipitfile.js └── sonar-scanner.template ``` Nossa estrutura de pastas será exatamente essa: ``` front/ ├── public ├── index.html ├── src/ ├── assets/ ├── scss/ * local onde os scss deverão ser criados ├── layout/ ├── index.scss ├── main.scss ├── core/ ├── helpers/ * helpers uteis ao sistema ├── filters.js ├── functions.js ├── index.js ├── validation.js ├── index.js ├── layouts/ ├── default.vue * onde todas as rotas irão ser renderizadas ├── pages/ ├── components/ ├── alert/ * componente de alert geral do sistema ├── footer/ * componente do footer ├── loader/ * componente de loader geral do sistema ├── logotype/ * componente do logotipo da loja ├── shop-site/ * componente onde renderiza o seletor de site ├── sidebar/ * componente onde renderiza o topbar e sidebar com o menu do admin ├── login/ ├── index.vue * página de login do sistema ├── index.vue * onde renderiza os componentes ├── plugins/ * pasta plugins do sistema ├── loader.js ├── vuetify.js ├── router/ * pasta router ├── index.js ├── server/ * pasta axios ├── index.js ├── store/ * pasta vuex (aqui deverão ser criados todos os stores das telas do sistema, com o nome da pasta com o nome da tela em ingles) ├── auth/ ├── actions.js ├── index.js ├── mutations.js ├── state.js ├── sidebar/ ├── actions.js ├── index.js ├── mutations.js ├── state.js ├── sys/ ├── actions.js ├── index.js ├── mutations.js ├── state.js ├── actions.js ├── getters.js ├── index.js ├── mutations.js ├── state.js ├── App.vue ├── globalComponents.js * arquivo dos componentes globais ├── globalPages.js * arquivo dos componentes de páginas globais ├── main.js ``` ## Vue Router **Sempre lembrar de utilizar a estrutura de código encontrada dentro do arquivo router.js**: ``` [js] import Vue from 'vue' import VueRouter from 'vue-router' import routes from 'vue-auto-routing' import { createRouterLayout } from 'vue-router-layout' Vue.use(VueRouter); const RouterLayout = createRouterLayout(layout => { return import('@/layouts/' + layout + '.vue') }); import Login from '@/pages/login'; const router = new VueRouter({ routes: [ { path: '/', component: RouterLayout, children: routes }, { path: '/pages/login', component: Login, children: routes } ] }); export default router ``` Na pasta raiz `front/`, temos um arquivo de configuração do vue `vue.config.js` com os seguintes dados: ``` [js] module.exports = { outputDir: '../src/web/vue', publicPath: process.env.NODE_ENV === 'development' ? '/' : '/web/vue/', devServer: { 'disableHostCheck': true }, pluginOptions: { autoRouting: { pages: 'src/pages', importPrefix: '@/pages/', chunkNamePrefix: 'page-' } }, transpileDependencies: [ 'vuetify' ] } ``` O parâmetro `outputDir` será o local onde será compilado o vue. O parâmetro `publicPath` será a pasta raiz do vue no projeto. O parâmetro `autoRouting` dentro de `pluginOptions` será onde definimos a pasta raiz que será usada para o auto routing. Para as demais rotas que serão criadas de arquivos novos do sistema, podemos seguir o padrão a seguir: ``` [js] { path: '/pages/login', component: Login, children: routes } ``` ## Vuetify Caso seja necessário alterar as configurações de estilização e cores conforme a loja que for implementada, temos o arquivo localizado em `/front/src/plugins/vuetify.js`, nele existem as configurações do template utilizado para nossa aplicação: ``` [js] import Vue from 'vue'; import Vuetify from 'vuetify/lib'; Vue.use(Vuetify); /* * Caso precise modificar a identidade visual do projeto, por favor, * modifique apenas as cores abaixo, sem alterar nome de propriedade. * Para mais referencias de cores, acesse: https://vuetifyjs.com/en/styles/colors/ */ export default new Vuetify({ theme: { dark: false, default: 'light', themes: { light: { primary: '#3E1A58', secondary: '#E18719', accent: '#666666', error: '#b71c1c', info: '#2196F3', success: '#4CAF50', warning: '#FB8C00', }, dark: { primary: '#2196F3', secondary: '#424242', accent: '#FF4081', error: '#FF5252', info: '#2196F3', success: '#4CAF50', warning: '#FB8C00', }, }, }, }); ``` ## Axios Dentro da pasta `/front/src/server` temos o arquivo de configuração do axios, onde nele podemos preparar nossa aplicação para fazer as requisições ao backend do sistema, padronizamos da seguinte forma: ``` [js] import axios from 'axios' /* * Em caso de desenvolvimento local (watcher ligado) descomentar as opções: * baseURL: process.env.VUE_APP_URL, * OBS: NÃO ENVIAR DESCOMENTADO ESSAS OPÇÕES EM SEU COMMIT */ export default axios.create({ // baseURL: process.env.VUE_APP_URL, baseURL: '/', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, withCredentials: true, credentials: "same-origin", }) ``` Qualquer configuração que precise atualizar de conexão com a api, deverá ser atualizado neste arquivo. ## Vuex Para que o nosso sistema seja reativo, utilizamos o plugin `vuex`, definido no nosso `main.js`. Em cada store, deverá ser criado da seguinte estrutura: **exemplo store de autenticação do login** #### Arquivo de Action ``` [js] import axios from "@/server" export default { login({ commit }, payload) { return new Promise((resolve, reject) => { axios.post("/Login/Logar", payload, { headers: { 'Content-Type': 'multipart/form-data' } }) .then((response) => { commit('SET_LOGIN_RESPONSE', response.data); * usado para mudar o state de resposta do login resolve(response); }) .catch((error) => { reject(error) }) }) }, ... } ``` #### Arquivo de Mutations ``` [js] export default { SET_CHANGE_PASSWORD(state, data) { state.needsNewPassword = data; }, SET_LOGIN_RESPONSE(state, data) { state.loginResponse = data; }, } ``` #### Arquivo de State ``` [js] export default { needsNewPassword: false, loginResponse: {}, } ``` #### Arquivo de Index ``` [js] import actions from './actions' import state from './state' import mutations from './mutations' export default { state, actions, mutations, namespaced: true } ``` Logo após tal implementação é necessário importar o state e exportar no index do vuex para poder utilizar em todas as telas: #### Arquivo index do store ``` [js] import Vue from 'vue' import Vuex from 'vuex' import state from "./state" import getters from "./getters" import mutations from "./mutations" import actions from "./actions" Vue.use(Vuex); import sidebar from './sidebar' import auth from './auth' export default new Vuex.Store({ getters, mutations, state, actions, modules: { sidebar, auth }, strict: process.env.NODE_ENV !== 'production' }) ``` #### Exemplo de uso em um componente vue ``` [vuejs] login() { this.$loader.init(); this.$store.dispatch('auth/login', this.formatData()) .then(() => { this.$loader.stop(); let data = this.$store.state.auth.loginResponse; if (data.token) { this.withToken = data.token; this.typeAlert = 'success'; this.messageAlert = 'O token de acesso foi enviado para seu e-mail!'; } else { this.$router.push('/'); } }) .catch(error => { this.$loader.stop(); this.typeAlert = 'error'; this.messageAlert = error.response.data.errors.reason; }) }, ``` Neste exemplo vemos como usamos o `this.$store.dispatch` para dispachar um evento de store da função do `auth/login`, onde o mesmo chama a função do store e a executa como uma `Promise`. e para recuperar os dados da requisição utilizamos o state populado pela `Promise` da seguinte maneira, `this.$store.state.auth.loginResponse`. Obs: Todas os métodos devem ter o mesmo nome da função definida no action do store, ou seja, neste exemplo, o método criado no componente se chama `login()` e o do store se chama também `login()`. ## Helpers customizaveis do sistema Como vimos anteriormente, no core do sistema temos a pasta de helpers, nele temos todos os arquivos de utilitarios do sistema, onde são totalmente customizáveis e expansíveis, ex do que já temos: - filters: ``` [js] import Vue from 'vue' Vue.filter('capitalize', function (value) { if (!value) return ''; value = value.toString(); let arr = value.split(" "); let capitalized_array = []; arr.forEach((word) => { let capitalized = word.charAt(0).toUpperCase() + word.slice(1); capitalized_array.push(capitalized) }); return capitalized_array.join(" "); }); Vue.filter('title', function (value, replacer="_") { if (!value) return ''; value = value.toString(); let arr = value.split(replacer); let capitalized_array = []; arr.forEach((word) => { let capitalized = word.charAt(0).toUpperCase() + word.slice(1) capitalized_array.push(capitalized) }); return capitalized_array.join(" "); }); ``` Para utilizar os filtros disponíveis neste arquivo, basta chamálos externamente em seu componente da seguinte forma: ``` [vuejs] {{ message | title }} ``` - functions: ``` [js] class Functions { ... isValidFileType(fn, validType) { let i, p, e; if (typeof fn != 'string') return false; p = fn.lastIndexOf('.'); if (p < 0 || ++p >= fn.length) return false; e = fn.substr(p); for (i = 0; i < validType.length; i++) if (e == validType[i]) return true; return false; } } export default new Functions(); ``` - validation: ``` [js] import functions from './functions'; export default { rules: { required: v => v.length > 1 || 'Este campo é obrigatório', email: v => v.length > 1 && /.+@.+/.test(v) || 'Digite um email válido', min: function (v, l) { return v.length >= l || `Este campo deve conter no mínimo ${l} dígitos` }, max: function (v, l) { return v.length <= l || `Este campo deve conter no máximo ${l} dígitos` }, equals_length: function (v, l) { return v.length == l || `Este campo deve conter ${l} dígitos` }, file_type: v => functions.isValidFileType(v) || 'Tipo de arquivo inválido', } }; ``` Aqui temos uma implementação um pouco diferente, onde todas as funções e regras de validação ficarão disponíveis para uso a partir da nossa propriedade global `$helper`, na qual iremos explicar mais pra frente. Caso seja necessário implementar alguma validação, função ou filtro novo, fiquem a vontade para incrementar esses arquivos. Mas caso precise criar outro tipo de helper que não seja um desses já criados, será necessário importar no arquivo `index.js` do nosso helper para que fique disponível no sistema como um todo. ## Uso dos componentes padrões do sistema Atualmente possuimos alguns componentes que podem ser utilizados em todo o sistema da seguinte forma: **Componente alert:** ``` [vuejs] <template> <v-row v-if="messageAlert != ''"> <alert :type="typeAlert" :message="messageAlert"/> </v-row> </template> <script> export default { data() { return { messageAlert: '', typeAlert: '', } } } </script> ``` Dessa forma, o alerta só será exibido caso exista algum conteúdo no mesmo, que é passado pela variável `messageAlert`. Para utilizar esse componente, é preciso passar algumas propriedades: - type - Obrigatório: sim - Tipo: string - Valor default: '' - Ex: `primary, secondary, accent, error, info, success e warning` - message - Obrigatório: sim - Tipo: string - Valor default: '' - Ex: 'Seja bem vindo ao Admin!' Todas as cores das opções para o tipo podem ser definidos no arquivo de configuração do [vuetify](#Vuetify). **Componente dialog:** ``` [vuejs] <template> <view-dialog :dialog="dialog" :title="title" :text="message" :actions="true" :btnConfirmAction="close" btnConfirmText="Ok" /> </template> <script> export default { data() { return { dialog: false, title: '', message: '', color: '' } }, methods: { show() { this.dialog = true; this.color = 'error'; this.title = 'Aviso!'; this.message = 'Ocorreu um erro ao deslogar! Tente novamente ou recarregue a pagina.'; }, close() { this.dialog = false; } } } </script> ``` Para utilizar esse componente, é preciso passar algumas propriedades: - dialog - Obrigatório: sim - Tipo: boolean - Valor default: false - maxWidth - Obrigatório: não - Tipo: [string, number] - Valor default: '300' - title - Obrigatório: não - Tipo: string - Valor default: '' - text - Obrigatório: sim - Tipo: string - Valor default: '' - actions - Obrigatório: sim - Tipo: boolean - Valor default: false - btnCancelAction - Obrigatório: não - Tipo: function - Valor default: `null` - btnCancelText - Obrigatório: não - Tipo: string - Valor default: 'Cancelar' - btnCancelColor - Obrigatório: não - Tipo: string - Valor default: 'warning' - btnConfirmAction - Obrigatório: não - Tipo: function - Valor default: `null` - btnConfirmText - Obrigatório: não - Tipo: string - Valor default: 'Confirmar' - btnConfirmColor - Obrigatório: não - Tipo: string - Valor default: 'success' **Componente logotype:** ``` <template> <div> <logotype height="300"/> </div> </template> ``` Para utilizar esse componente, é preciso passar a seguinte propriedade: - height - Obrigatório: não - Tipo: string - Valor default: '100%' **Componente site:** ``` <template> <div> <shop-site/> </div> </template> ``` Para utilizar esse componente, basta chamar em sua página da maneira acima e ele irá renderizar, caso necessite do retorno do site selecionado, você deverá colocar a chamada de bind a seguir, passando uma função callbak para coletar o resultado: ``` <shop-site @set-site-value="onSetSiteValue"/> ``` Dessa forma en sua função callback, você poderá coletar o valor selecionado dessa forma: ``` onSetSiteValue(payload) { this.site = payload; } ``` **Componente combo de categorias:** ``` <template> <div> <combo-store :site-id="site" @set-category-value="afterSelectCategory"/> </div> </template> <script> export default { data() { return { site: '', categorySelected: '' } }, methods: { afterSelectCategory(payload) { this.categorySelected = payload; } } } </script> ``` Para utilizar esse componente, é preciso passar a seguinte propriedade: - SiteId - tipo: [String, Number] - default: 0 - Obrigatório: sim Essa propriedade é necessária para efetuar a busca das categorias conforme a escolha do site selecionado no componente seletor do mesmo. O componente por sua vez, emite um evento no qual é capaz de coletar os dados da loja/categoria/subcategoria selecionado, `@set-category-value`, ele recebe uma função de callback onde é possível coletar o valor selecionado para uma variável no componente pai. Para visualizar todos esses componentes funcionando na prática e o seu layout, basta acessar a página de teste criada pelo admin `Marketing/Envelopamento/Componente Vue` ou pelo código em `pages/page-test/index.vue`. Caso seja necessário implementar algum componente novo, será necessário, por questão de padronização, importar o mesmo no arquivo `globalComponents.js` para que fique disponível no sistema como um todo, como os demais. Ex: ``` [js] import Vue from 'vue'; import Loader from '@/pages/components/loader'; import Alert from '@/pages/components/alert'; import Dialog from '@/pages/components/dialog'; import Logo from '@/pages/components/logotype'; Vue.component(Loader.name, Loader); Vue.component(Alert.name, Alert); Vue.component(Dialog.name, Dialog); Vue.component(Logo.name, Logo); ``` ## Uso das props globais **Prop de loader:** ``` [vuejs] <script> export default { methods: { sendMessage() { this.$loader.init(); // inicia o loader this.$store.dispatch('message') .then(() => { this.$loader.stop(); // finaliza o loader }) } } } </script> ``` Neste exemplo ficticio, vemos como podemos ligar e desligar o loader da página. temos nossa variável global `$loader` que nos permite iniciar `init()` ou finalizar `stop()` o loader da página. **Prop de helper:** [Como falamos anteriormente](#Helpers-customizaveis-do-sistema), nossos helpers criados para auxiliar nosso dia a dia, estarão disponíveis a partir da prop `$helper` no sistema inteiro. Com ela vamos poder utilizar nossas funções gerais e nossas regras de validação no sistema como um todo, por exemplo: ``` [vuejs] <template> <v-text-field v-model="token" :rules="[ rules.equals_length(token, 6), rules.required ]" ></v-text-field> </template> <script> export default { data() { return { rules: this.$helper.validation.rules } }, ... } </script> ``` Neste exemplo, estamos utilizando nosso helper para nos trazer as validações genericas de nosso sistema, dessa forma podemos utilizar no `v-text-field` como regra de validação de formulário. ## Padronização no backend do admin No Controller base do nosso admin `src/core/Controller.php`, criamos o método para renderizar as páginas em Vue: ``` [php] function RenderVue($vueRoute) { $this->vueRoute = "#{$vueRoute}"; View::Render("Page/VueIframe", true); } ``` O parâmetro `$vueRoute` receberá o path da componente à ser renderisado. Exemplo: `produtos/index` Agora vamos até o diretório `Page` das nossas views `src/app/views/Page` onde criamos o arquivo `.phtml` que irá renderizar o vue pelo iframe: > VueIframe.phtml ``` [phtml] <? // Identifica automaticamente se o hot reloading est ativo e // define a URL de acordo. if (getenv('AMBIENTE') === 'LOCAL' && @fsockopen('localhost', 8080)) { $vueUrl = 'http://localhost:8080'; } else { $vueUrl = '/web/vue/'; } $vueUrl .= $this->vueRoute; ?> <iframe src="<?= $vueUrl ?>" frameborder="0" style="width: 100%; height: 100%;" /> ``` A condição para definir a url serve para caso precisemos utilizar o `hot reloading` do vue para desenvolvimento pelo comando `npm run serve`. Para isso precisaremos também liberamos a porta 8080 no docker e liberamos o acesso no arquivo `src/index.php`: > src/index.php > Adicionamos as linhas abaixo: ``` [php] // Necessário pra usar hot-reload do Vue localmente if (getenv('AMBIENTE') == 'LOCAL') { header('Access-Control-Allow-Origin: http://localhost:8080'); header('Access-Control-Allow-Credentials: true'); header('Access-Control-Allow-Headers: cache-control, content-type'); } ``` > docker-compose.yml > Adicionamos a linha `- "8080:8080"` nas portas `ports` ``` [yml] version: "2" services: app: image: registry.i9xp.com.br:8082/docker/php-apache:${DOCKER_TAG} container_name: projeto-admin volumes: - .:/var/www/html/app env_file: - .env ports: - "8081:80" - "30204:443" - "8080:8080" ``` e pronto! ## Funcionamento do hot reload em ambiente de desenvolvimento Para utilizar o hot reload para trabalhar em ambiente de desenvolvimento local, é preciso acessar a pasta do admin e rodar o seguinte comando: `npm run dev`. Porém, é importante que antes disso seu docker tenha subido corretamente, na raiz de cada projeto, sempre teremos um arquivo chamado `env.sh`, atualizamos ele para que fosse possível dar build no admin ao subir os containers. Abaixo o exemplo de script atualizado: > env.sh ``` [sh] #!/bin/bash #read funcao funcao=$1 diretorios=("$(pwd)/admin" "$(pwd)/site") min=0 max=$(( ${#diretorios[@]} -1 )) clear echo "*** Inicio ***" case "$funcao" in down) echo while [[ max -ge min ]] do bold="\033[1m${diretorios[$max]}\033[0m"; echo -e "Finalizando containers em ${bold}" cd "${diretorios[$max]}" docker-compose down (( max-- )) echo done ;; up) while [[ min -le max ]] do bold="\033[1m${diretorios[$min]}\033[0m"; echo -e "Iniciando containers em ${bold}" cd "${diretorios[$min]}" docker-compose up -d npm run vue-install && npm run build (( min++ )) echo done ;; restart) sh ~/env.sh down sh ~/env.sh up ;; *) echo "Opção $funcao inválida. Use 'up' ou 'down'" ;; esac echo echo echo "-----------------------------------------------------------" echo -e "-- \033[1mLISTAGEM\033[0m --" echo "-----------------------------------------------------------" docker ps echo echo "*** Fim ***" ``` Com isso, ao rodar seu comando `./env up`, além dos containers do docker subirem, o frontend em vue será renderizado e estará pronto para utilizar em modo de produção. Caso precise em modo de desenvolvimento, entre na pasta do admin e rode o comando mencionado acima, mas lembre-se de ativar as opções do [axios](#Axios) mencionados anteriormente. ## Criação de uma nova tela Nesta seção vamos definir alguns padrões de construção das novas telas em vuejs para o admin. Primeiramente, devemos criar o caminho para a nossa nova tela no banco de dados, na tabela `Admin_Area`. Não vamos entrar em muitos detalhes aqui, pois essa parte de criação vamos seguir o padrão já existente, tendo uma pequena diferença, o campo `Url`, deverá conter o nome do componente que você irá criar, caso contrário, isso irá ocasionar um erro. Por padrão, as nomeclaturas de paginas iremos seguir com `page-` e concatenado com isso o nome da página, sem caractere especial e o mais específico possível, por exemplo: `page-envelopamento-gerenciar`. O campo `Legado` deverá ter o valor igual a `1`, dessa forma nosso sistema entenderá que o mesmo é um componente de página e não uma tela legada do `extjs`. Após essa etapa, devemos criar uma pasta em nosso repositório de `pages`, ex: ``` |---- pages/ |---- page-test/ |---- index.vue ``` Logo após a criação do componente, devemos declarar o mesmo em nosso arquivo de página global e definir um nome para que ele possa ser identificado com a "url" que foi definida na tabela `Admin_Area`, ex: > globalPages.js ``` [vuejs] import Vue from 'vue'; /* * Aqui definimos nossos componentes de telas */ Vue.component('page-component-test', () => import('@/pages/page-test')); ``` Para visualizar sua tela nova, basta atribuir a mesma ao seu grupo de usuario e abrir no admin.

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully