A ferramenta de colaboração de projeto de alta imitação [Worktile] leva você passo a passo do zero para realizar funções como estrutura organizacional, disco de rede, mensagem, projeto e aprovação

declaração

Este curso foi imitado do Worktile desenvolvido pela Beijing Yicheng Starlight Technology em 6 de agosto de 2020. Os direitos autorais dos materiais relevantes pertencem ao site original. Este curso é um trabalho de estação de imitação, apenas para ensino e não para fins comerciais. Para cooperação comercial , visite o site de origem.

Antes do desenvolvimento do curso, experimentei quase todas as plataformas corporativas de escritório colaborativo do mercado. Escolhi o Worktile porque achei que era o melhor na época e também gostei do estilo de UI. Eu o recomendaria para amigos que precisassem.

O objetivo deste curso é demonstrar um software de aplicativo SaaS colaborativo complexo do tipo OA, baseado em ZhongTouch, uma plataforma de aplicativo profissional de baixo código, como desenvolver um aplicativo SaaS complexo com funções completas. Worktile é um sistema de gerenciamento de colaboração em projetos especialmente projetado para cenários de escritórios corporativos, com muitos conceitos de negócios e relacionamentos intrincados; embora você provavelmente não precise fazer esse tipo de aplicação complexa, é muito valioso aprender as ideias e usos usados ​​nele .

O site de origem pode evoluir iterativamente ao longo do tempo, mas o funcionamento do curso não mudará de acordo, por isso é normal que os trabalhos sejam diferentes do site de origem que você vê agora.


Este curso possui um vídeo bem detalhado explicando cada módulo funcional, que é implementado passo a passo a partir de uma página em branco: visão geral , estrutura organizacional , disco de rede , mensagens instantâneas , gerenciamento de projetos e aprovação .

conta

Para demonstrar a estrutura organizacional da empresa, são fictícias as seguintes pessoas:

  1. CEO: Dai Guoqiang 13845678901 Administrador

  2. Gerente Geral Gerente de Escritório: Zhao Min 13845678911 Administrador

  3. Diretor de pessoal: Chen Ying 13845678902 administrador

  4. Diretor Financeiro: Fu Yuan 13845678906 Administrador

  5. Líder da equipe do projeto: Zeng Hui 13845678312

  6. Desenvolvimento da equipe do projeto: Huang Hongliang 13845678909

  7. Desenvolvimento da equipe do projeto: Huang Yaoqian 13845678223

  8. Diretor Técnico: Xie Linhua 13845678907

  9. Gerente da equipe do projeto: Sun Ronghao 13845678905

  10. Líder do grupo de plataforma: Ge Chuanfu 13845678910

  11. Líder da equipe de teste: Wang Boxiang 13845678908

  12. Departamento não atribuído: Huang Feikai 13845678102

Esses personagens e telefones celulares são todos inventados, e os avatares são das minhas anotações .

Este aplicativo de curso é usado como um modelo para os alunos clonarem, os dados não devem ser muito confusos, nenhuma conta de demonstração é fornecida e o registro não está aberto, portanto, mesmo que este aplicativo seja clonado, ele não poderá ser logado diretamente. Após a clonagem, vá para a página de login (/z/login), que usa o plug-in de gerenciamento de login da conta , clique com o botão direito para selecionar o plug-in, ative "Registrar" no painel direito, salve e atualize a página para registrar-se. Após o registro, você terá uma conta para fazer login, mas ainda não há senha para fazer login nas contas do pessoal acima. Você pode primeiro comentar as restrições em $user.toggleRole em "Segurança de back-end" e depois clicar "Configurações" de um membro em "Gerenciamento de Membros" e clique em "Modificar" Após modificar a senha deste membro na aba "Conta de Membro", a senha será logada como esta pessoa. Para obter orientação detalhada, vá para o vídeo de ensino.

Alunos que estão prontos para pesquisas aprofundadas, registre-se e faça login no site oficial do ZhongTouch , clique no botão clonar e copie o aplicativo inteiro para depurar e modificá-lo à vontade.

$V globais

O estado persistente global ssão as iniciais de status . Leia do localStorage quando o aplicativo for iniciado e salve no localStorage quando o aplicativo terminar (onbeforeunload), para que você possa restaurar imediatamente o estado quando o deixou da última vez.

  1. $Vs navegação à esquerda: o rótulo de navegação à esquerda mais recente

  2. Mensagem $Vs: o _id do último contato ou grupo de bate-papo privado

  3. $Vs não lidas: o número de mensagens não lidas mais recentes de cada chat ou grupo privado

  4. $Vs lidos: o horário da última mensagem lida de cada chat ou grupo privado

  5. $Vs recolhido: vários menus recolhidos

  6. Projeto $Vs: o último projeto aberto _id e seu subscrito de componente e subscrito de visualização

  7. Barra de tarefas $Vs: lista de tarefas fixada na barra de tarefas por projeto

evento global

  1. clique: Quando houver uma janela pop-up, $v.popfeche a janela pop-up, mas keepse for verdade, clique dentro da janela pop-up ou fora da zpage (confirmar ou alertar) para não fechá-la.

  2. keydown: Quando houver uma janela pop-up ou janela modal, pressione a tecla exit Escapepara fechar a janela pop-up ou janela modal.

Organização

A estrutura organizacional de uma empresa é a base para outras funções. Você pode gerenciar vários departamentos e seus membros em uma estrutura em árvore, e também pode verificar o status das tarefas que os colegas estão realizando no catálogo de endereços.

  1. A lista de tarefas
    pode ser adicionada, modificada, movida para cima, para baixo e excluída.

  2. Quando o usuário faz login ($c.exp.onLogin), são puxadas as informações de todos os funcionários da empresa e da estrutura organizacional da empresa, ou seja, o departamento $c.exp.

  3. A estrutura organizacional e de funcionários são integradas em variáveis ​​globais, o que é conveniente para uso em vários locais do aplicativo.
    $V.部门É um clone da estrutura organizacional, exceto pela adição de um "departamento não atribuído".Depois de
    $V.部门成员nivelar a estrutura organizacional, a adição de membros de cada departamento
    $V.部门总成员é baseada nos $V.membros do departamento e, em seguida, a adição de todos os membros descendentes do departamento.

  4. Gerenciamento de membros
    Configuração de informações básicas, modificação de conta de membro, configuração de função

  5. Ajustar o departamento
    Você pode selecionar membros (individuais ou todos) para ajustar seus departamentos em lotes

  6. Pesquisa de membros (filtro)

principal dificuldade:

  1. Mover é trocar posições. Funções nativas são comumente usadas. arr.splice(新下标, 0, arr.splice(旧下标, 1)[0])Primeiro, exclua um elemento do subscrito antigo, e o elemento excluído é uma matriz, então adicione o [0]próprio elemento mais tarde e, em seguida, insira o elemento excluído no novo subscrito.
    Também é fácil entender que $l.arr.splice($index - 1, 0, $l.arr.splice($index, 1)[0]) está subindo, $l.arr.splice($index + 1, 0, $l.arr.splice($index, 1)[0]) é movido para baixo.
    Depois de aprender os cursos a seguir e dominar como usar Sortable.js , alterar os botões para cima e para baixo aqui para arrastar a classificação tornará a operação mais fácil e intuitiva.

  2. Para deletar um elemento de um array no banco de dados , comumente utiliza-se $pull , ou seja, para extrair um elemento do array :
    $xtk.modify("Empresa", "Posição", {$pull: {"x.arr ":$x} })

  3. Estrutura de dados
    Os departamentos são armazenados em uma estrutura de árvore. O departamento tem um nome e um supervisor, e a matriz zchildren é usada para armazenar subdepartamentos.
    Quando a fonte de dados do componente de dados contém zchildren, ela será renderizada recursivamente em uma estrutura de árvore.

  4. Recuo de subdepartamento
    Para refletir a hierarquia departamental, o recuo à esquerda (paddingLeft) é calculado de acordo com a profundidade hierárquica ($indexes) no estilo dinâmico

  5. $v.F5Rerenderize permitindo que o componente de montagem obtenha os dados mais recentes após o ajuste do departamento ou atualização do membro

  6. O departamento que exclui membros usa é $unset. Fique atento ao departamento especial "departamento não atribuído".

  7. Para excluir subdepartamentos $pull, "afaste-se" da matriz.

 

livro de endereços

  1. lista de colegas

  2. Detalhes do colega

  3. Lista de tarefas do colega e número de status

  4. envie uma mensagem

  5. Contatos frequentes

  6. coletar

  7. Encontre colegas por departamento

netdisc

Semelhante ao Baidu Netdisk e ao Alibaba Netdisk, ele gerencia arquivos armazenados na nuvem na forma de pastas virtuais e carrega pastas inteiras em lotes.

Exibição de disco de rede

  1. Leia informações de pastas e arquivos.

  2. árvore de pastas

  3. caminho de arquivo

  4. procurar

  5. ordenar

principal dificuldade:

  1. Estrutura de dados em árvore
    As pastas são apenas registros simples armazenados na tabela de produtos, contendo informações 名称, 颜色, (ou seja, a pasta pai), em vez de uma estrutura de árvore direta, mas precisamos construir uma estrutura semelhante à lição anterior com base no " parent " fonte de dados do componente de dados .
    As pastas podem ter uma estrutura hierárquica muito profunda, que não é tão única e estável quanto a estrutura organizacional, e todas as pastas não podem ser lidas de uma só vez. Em onReady, primeiro lemos o arquivo de nível superior e a pasta de nível superior (sua pasta pai é "nenhuma") e lemos as respectivas subpastas diretas por vez (dependendo se há subpastas para determinar se devemos exibir o "expandir" ícone de pequeno triângulo).
    Um ponto importante para ler um arquivo ou pasta é passar o _id da pasta pai para o ambiente de execução: { 父: _id }.
    Um arquivo é um recurso comum carregado no servidor de armazenamento de objetos e não possui informações da árvore da estrutura do arquivo, portanto, precisamos adicionar a pasta atual como sua pasta pai à tabela de recursos sempre após o upload de um arquivo: $resource.modify (_id, {
    " x.type": "disco de rede", "x.parent": pasta atual_id })

  2. Várias variáveis ​​​​chave
    $v.根são a raiz da árvore da estrutura de pastas (o nome é "Disco de Rede", o primeiro nó do caminho do arquivo), que é a maior árvore. Seu “zchildren” contém as pastas de nível superior lidas acima e “files” contém os arquivos de nível superior. Em particular, o seu _id é "nenhum".
    $v.树É uma árvore grande e pequena começando em todos os níveis de pastas com a pasta pai _id como chave. Então $v.树.无é isso $v.根.
    $v.路径é a matriz de árvores de pastas em todos os níveis, desde a raiz da árvore até a
    $v.文件夹pasta atual, é o folder_id atual. Quando o usuário clica (seja na árvore de estrutura de arquivos à esquerda ou na lista de pastas principal), ele a usará como pasta pai para ler as informações da pasta e do arquivo nela contida. Além disso, ao clicar no ícone do pequeno triângulo "expandir", você só precisa ler e escrever as informações da pasta neta. Por um lado, as informações do arquivo não são exibidas na árvore e, por outro lado, o subpasta foi lida. As informações da pasta neta servem para determinar se a subpasta deve ser exibida. Pequeno triângulo para pastas.


  3. Cor dinâmica da pasta (componente personalizado)

  4. Rerenderização Existem três componentes montados que remontarão os dados e serão renderizados novamente quando alterados
    . A. O componente de montagem na árvore de estrutura de arquivos à esquerda é usado para montar , começando no nó raiz e percorrendo recursivamente todas as pastas pesquisadas: por um lado, monte diretamente a pasta, por outro lado, empurre-a para o Matriz zchildren da pasta pai. B. O componente de montagem no caminho do arquivo na parte superior é usado para montagem , recursivamente da pasta atual até o nó raiz e empurra cada camada de pastas para o topo da matriz de caminho (unshift). C. O componente de montagem na lista de arquivos principal é usado para montar a lista de arquivos na pasta atual.$v.F5
    $v.树$v.树
    $v.路径

  5. Pesquisando e classificando
    Sempre exibimos primeiro a lista de pastas e depois a lista de arquivos, que são pesquisados ​​​​e classificados separadamente, e então os dois são unidos:
    ($v.search.folder|| $v.tree[$v . pasta].zchildren || []). sort ($v.sort.key, $v.sort.incr).concat ( ($v.search.file || $v.tree[$v.folder] . arquivo|| []). classificar ($v.sort.key, $v.sort.incr))

 

Upload de disco de rede

  1. nova pasta

  2. Carregar arquivos (múltiplas escolhas)

  3. Pasta de upload (mantém a estrutura do arquivo)

  4. Lista de upload, progresso e status do upload

principal dificuldade:

  1. O
    botão de upload de arquivo aciona o elemento de entrada oculto: $el.firstElementChild.click().
    Após o upload ser bem-sucedido, $l.Uele é definido no início da seleção do arquivo (onChange) para evitar que o usuário clique em outras pastas durante o processo de upload, o que fará com que a pasta $ v. seja alterada e atualize o arquivo atual do usuário List ($v.exp.file.exc({parent: $v.folder})) não tem essa preocupação.

  2. Carregar pasta
    Para evitar ser afetado pela mudança de $v. pasta, primeiro atribua-a a uma variável temporária: $l. pasta = $v. pasta. A chave para carregar recursivamente a pasta inteira é que cada arquivo a ser
    carregado tem um caminho relativo webkitRelativePath. Percorra cada nó do caminho, crie nós de pasta não criados, até que a pasta mais interna seja atribuída à pasta atual e use-a como pasta pai após a conclusão do upload.

  3. Lista de upload
    Coloque as informações na $v.上传matriz antes do upload do arquivo, marcado como "upload", se for uma imagem ou vídeo, crie uma miniatura para ele: URL.createObjectURL (arquivo), altere seu estilo de progresso no tempo durante o upload processo, após a conclusão do upload Remova o sinalizador "upload".

Gerenciamento de disco de rede

  1. nomeação dupla

  2. mover

  3. excluir

  4. modificar cor

  5. enviar para bate-papo

  6. Lixeira de reciclagem

principal dificuldade:

  1. Excluímos uma pasta ou arquivo para retirá-lo de seu local original, mas depois há uma operação de restauração, portanto, as informações no local original devem ser retidas. Implementamos $rename: {"x.parent": "x.original parent"}
    alterando a pasta pai para a pasta pai original, para que os recursos sem pastas pai sejam excluídos logicamente. "x.Pai original": {$existe: verdadeiro}


mensagem instantânea

Semelhante às mensagens instantâneas corporativas WeChat/Feishu/DingTalk, ele suporta bate-papo privado, bate-papo em grupo, mensagens históricas, fixação no topo, pode enviar emoticons, imagens, arquivos de disco de rede, projetos e aprovações para conversas, e também pode marcar arquivos e fixos mensagens. É um ótimo curso para aprender sobre conexões.

inicialização de mensagem

  1. Obtenha as informações da lista de grupos privados (os membros do grupo incluem eu) e grupos públicos e faça uma lista de seus _ids $V.消息.群ID.

  2. Abra a conexão $socket.open():
    group_id list as channels, para que você possa receber mensagens de grupos relacionados;
    saveToDBdefina-o como true, salve as informações no banco de dados, para que você possa visualizar o histórico de mensagens através de $socket.hist(), mesmo se você não estiver online, é enviado por outra pessoa As mensagens não lidas também podem ser recebidas;
    allowMultiLogindefina-o como verdadeiro, para que vários dispositivos/navegadores abertos possam receber mensagens ao mesmo tempo.

  3. onConnect foi conectado
    obtendo $socket.hist()todas as mensagens do histórico de chats privados.
    Obtendo $socket.hist($V.消息.群ID)mensagens históricas de todos os grupos públicos e privados.
    Adicione a data da mensagem enviada a cada mensagem e exiba a data e a semana para divisão na lista de mensagens por conveniência.
    Obtenha uma lista de _id's das sessões fixas.
    De acordo com o objeto de bate-papo privado _id ou bate-papo em grupo _id:
    coloque cada lista de mensagens em $V.消息.chat;
    coloque cada lista de mensagens a mais em $V.消息.more, e sempre que a janela de bate-papo rolar para o topo, retire um _id dela para obter mensagens históricas anteriores;
    coloque cada mensagem _id nele $V.消息.histIDe use-o ao excluir mensagens históricas; coloque o número de mensagens
    cujo tempo de envio de cada mensagem é maior que seu tempo de leitura ;$V.s.已读$V.s.未读

  4. onData recebe uma nova mensagem
    e coloca a mensagem na lista de mensagens correspondente de acordo com o objeto de chat (chat privado object_id ou chat_id de grupo) $V.消息.chat.
    Se a mensagem estiver na sessão de bate-papo atualmente aberta, a nova mensagem será exibida na visualização após a renderização; caso contrário, o número de mensagens não lidas será aumentado em 1.
    Se esta mensagem contiver informações @my, uma janela de notificação aparecerá (se não estiver bloqueada).

sessão de mensagens

  1. Criar configurações de grupo/grupo

  2. juntar-se ao grupo

  3. Inicie um bate-papo privado

  4. lista de conversas. Independentemente de haver mensagens na sessão superior, todas as mensagens são exibidas primeiro e, em seguida, a sessão com mensagens, exceto a superior, é exibida:
    $V.message.top.concat($V.message.chat.keys() .filter('!$V.message .top.includes($x)'))

  5. Filtragem de sessão:
    .filter('$f.search.kw ? ($c.user[$x].x.name|| $c.xdb[$x].x.name).includes($f.search. kW): 1')

  6. Mensagens não lidas, incluindo histórico de mensagens não lidas e novas mensagens não lidas instantâneas.
    Total não lido: $Vs unread.values().reduce('$acc + $x', 0)

  7. Ao clicar na sessão
    , atribua o ID da sessão à mensagem $Vs, renderize a lista de mensagens e role até a última mensagem
    Se o número de mensagens não lidas for menor que 6, defina todas as mensagens como lidas (limpe as não lidas e registre o horário de envio da última mensagem)

  8. excluir sessão
    A. excluir a lista de arquivos da sessão associada
    B. excluir o próprio arquivo da lista de arquivos
    C. excluir a lista de mensagens fixadas da sessão associada
    D. excluir todo o histórico de mensagens.

mensagem instantânea

  1. Lista de mensagens
    $V.message.chat[$Vsmessage]

  2. Mostrar divisão de data quando a data não for igual à da mensagem anterior.
    data!== $array[$index - 1].data

  3. Alinhe a mensagem enviada por você à direita para distingui-la da mensagem enviada por outros
    $c.me._id === from ? " message-item--me" : ""

  4. Caixa pop-up de mensagem
    Mensagem corrigida, exclua a mensagem enviada por você mesmo

  5. HTML组件Renderize o corpo da mensagem com

  6. Observador Cruzado
    Se houver mais mensagens para serem lidas ($V.message.more[$Vsmessage].length), ao rolar até o topo da caixa de mensagem, o observador cruzado irá acionar o observador cruzado para pegar um item dela e entregá-lo $V.消息.moresobre $socket.more() para obter mais mensagens e colocá-las no topo da lista de mensagens, role até a primeira mensagem após a renderização, mas o visualizador cruzado está fora da janela, para evitar o disparo contínuo, mas espere pelo usuário para ir mais longe Ele será acionado novamente ao rolar para cima.
    Um ponto de atenção é introduzir uma variável temporária $vlmore para evitar a primeira execução causada por cada nova renderização do visualizador cruzado, porque ela não é causada pela rolagem ativa do usuário para cima neste momento.

  7. Mensagens não lidas
    Exibe o número de mensagens não lidas, quando clicado, role até a última mensagem lida ou o topo da caixa de mensagem para acionar um observador cruzado para mais mensagens e, em seguida, acione "marcar todas como lidas"

  8. Envie uma mensagem de texto
    e [表情文本]substitua-a pela imagem correspondente: .replace($c.reg.emoticon, $c.fun.emoticon);
    @someone: Quando @ é pressionado em uma conversa em grupo (ou seja, Shift + 2, o keyCode de 2 É 50) Uma lista de membros do grupo excluindo você mesmo aparecerá, mas quando a janela for exibida, pressione uma tecla diferente de Shift (keyCode é 16) para fechar a janela pop-up; substitua-a pelo nome
    correspondente @24位的某人_id: .replace($c.reg.mention , $c.fun.mention); há 成员uma janela pop-up ao clicar porque o evento onClick é adicionado: window.mention click();
    $socket.send($Vs message, "texto", $f.mensagem.txt );

  9. Enviar mensagem de imagem/arquivo
    Clique no ícone do arquivo para abrir uma caixa de diálogo de upload, selecione um ou mais arquivos e carregue-os no servidor;
    em seguida, converta-os em textos HTML correspondentes de acordo com os diferentes tipos de arquivo: ipara imagens, exiba diretamente as miniaturas das imagens; vpara vídeos, exibir a captura de tela do vídeo correspondente; fpara outros tipos de arquivo, ícones diferentes serão exibidos de acordo com o formato do sufixo do arquivo);
    após o envio da mensagem, as informações do arquivo serão adicionadas à lista de arquivos desta sessão.
    Vale ressaltar que o banco de dados não é operado imediatamente após o envio da mensagem, mas o conteúdo é colocado na variável $v para ser salvo no arquivo mensagem, e então executado em $c.exp.onData após o retorno da mensagem , pois considera-se que quando o arquivo for excluído no futuro Ele também deverá ser excluído do histórico de mensagens. Ao excluir, é utilizado o horário de envio da mensagem d: $socket.pull(_id, from, d), e desta vez é gerado no lado do servidor e só é conhecido após o recebimento da mensagem.

  10. Enviar mensagem de tarefa/aprovação/disco de rede do projeto

outro

  1. Lista de arquivos Os
    arquivos enviados do disco de rede para o chat não estão nesta lista.
    Para excluir um arquivo:
    A. Exclua o próprio arquivo
    B. Remova da lista de arquivos da sessão associada
    C. Remova a mensagem correspondente do histórico de mensagens .

  2. Lista de mensagens fixas
    Removida da lista de mensagens fixas da conversa associada quando excluída

  3. Clique no ícone do membro
    para exibir os membros do grupo, você pode exibir ainda mais as informações do membro ou remover o membro.
    Exibir as informações do membro no bate-papo privado

  4. Clique no ícone de configurações
    Fixar e desafixar conversas
    Configurações do grupo
    Sair de um grupo
    Excluir um grupo

gerenciamento de projetos

O gerenciamento da colaboração em projetos é a função central do Worktile, que é usado para planejar, organizar e acompanhar algumas tarefas inter-relacionadas com um propósito claro. Suporta configurações personalizadas altamente flexíveis para se adaptar às necessidades do projeto em vários cenários.

Worktile planeja, controla e acompanha tarefas em diversas áreas e cenários de trabalho de maneira baseada em projetos.

Conceitos de gerenciamento de projetos

projeto

O centro de coleta de tarefas é utilizado para planejar, organizar e acompanhar algumas tarefas inter-relacionadas com um propósito claro. Os projetos podem ser de longo ou curto prazo.

componentes

O método de exibição de tarefas apresenta informações de tarefas aos usuários em diferentes formas visuais.Existem vários tipos de componentes: kanban, lista, tabela, gráfico de Gantt, calendário e relatório.

visualizar

Os componentes podem ter várias visualizações para agrupar, filtrar e classificar tarefas.

tipo de tarefa

O modelo que transporta diferentes cenários de negócios é composto por atributos de tarefas altamente estruturados, que podem especificar o relacionamento entre os tipos de tarefas e o modo de fluxo de estado, formando assim uma especificação para armazenamento de informações e colaboração em equipe.

Atributo : O tipo de tarefa pode especificar quais informações personalizadas este tipo de tarefa pode conter. Existem vários tipos de atributos: texto, rich text, número, data, membro, vários membros, seleção única suspensa, seleção múltipla suspensa, herança .

Tarefa

Uma instância de um tipo de tarefa. Cada tarefa possui título, status, responsável, participantes, horário de início e término, comentários, etc. A parte principal são os diversos atributos definidos no tipo de tarefa.

É recomendado que você tente expandir mais tipos de atributos após clonar este curso e até mesmo expandir tipos de componentes.

modelo de dados

projeto:

{
    名称: String,
    任务类型: [ID],
    描述: String,
    颜色: String,
    置顶: Boolean,
    参与人: [ID],
    组件: [{
        类型: String,
        名称: String,
        任务类型: ID,
        PC显示: [String],
        视图: [{
            名称: String,
            分组: String,
            筛选: String,
            排序: String
        }]
    }]
}

Tipo de tarefa:

{
    名称: String,
    项目: ID,
    父任务类型: ID,
    图标: String,
    描述: String,
    状态: [{
        名称: String,
        颜色: String,
        类型: Number,
        默认: Boolean
    }],
    状态流: {},
    属性: [{
        字段: String,
        类型: String,
        选项: [],
        新建: Boolean,
        必填: Boolean
    }]
}

Tarefa:

{
    标题: String,
    项目: ID,
    任务类型: ID,
    父任务: ID,
    关联任务: [ID],
    负责人: ID,
    开始时间: Number,
    截止时间: Number,
    评论: [{
        txt: String,
        auth: ID,
        d: Date
    }],
    状态: {
        名称: String,
        颜色: String,
        类型: Number
    },
    属性: { ... } // 例子如下
}

Entre eles, o atributo tarefa serve para armazenar os dados específicos especificados pelo atributo do tipo de tarefa a que pertence, como o atributo da tarefa de recrutamento:

{
    职位: String,
    部门: String,
    薪资范围: String,
    需求人数: Number,
    在职人数: Number,
    JD描述: String,
    参与人: [ID]
}

Instale o plugin de gerenciamento de banco de dados para uma compreensão mais intuitiva do modelo de dados.

inicialização

carregar projeto

  1. Carregar projetos privados para os quais contribuo

  2. carregar projeto público

  3. Combine os dois itens acima e extraia informações breves para formar $v.项目.菜单uma lista

  4. $v.项目.菜单filtrar $v.项目.置顶a lista de

  5. Se houver $V.s.项目._id, continue carregando as informações deste projeto

  6. O tipo de tarefa para carregar este item

  7. Carregar todas as tarefas deste projeto

  8. 任务栏a matriz para restaurar este item

Classificação de tarefas

Organize os dados brutos carregados acima em arquivos $v.任务.

  1. Se houver um tipo de tarefa pai especificado no tipo de tarefa, adicione-o ao seu $v.任务.子任务类型tipo de tarefa pai

  2. Atribua os atributos personalizados em cada tipo de tarefa $v.任务.属性à lista correspondente

  3. Para facilitar a leitura, continue atribuindo a lista de atributos acima ao $v.任务.字段objeto correspondente de acordo com os campos nela contidos

  4. Sem alterar a constante $c original, clone todos os dados da tarefa e atribua-os ao array, e forme objetos $v.任务.arrde acordo com seu _id$v.任务.O

  5. Se uma tarefa tiver uma tarefa pai, adicione a tarefa à matriz de subtarefas de sua tarefa pai de acordo com o tipo de tarefa x.子任务e procure recursivamente a cadeia de tarefas pai para formar uma matriz de cadeia de tarefas x.父任务arr, que é exibida como uma barra de navegação no parte superior da janela pop-up da tarefa

  6. Para o atributo do tipo de herança definido no tipo de tarefa, adicione um campo de herança a este atributo em todas as tarefas abaixo dele

Agrupamento

Organize os dados brutos carregados acima em$v.分组

  1. Atribua o componente do projeto atualmente ativo (ou seja, componente $Vs project.) para$v.分组.组件

  2. Depois que as tarefas no tipo de tarefa do componente ativo atual são filtradas pelos requisitos de filtragem da visualização ativa atual, elas são agrupadas de acordo com o campo de agrupamento especificado pela visualização ativa atual e cada tarefa é adicionada à matriz e diferentes agrupar atributos formando $v.分组.O[分组属性]um $v.分组.arrarray

  3. Conte o número de tarefas em cada grupo de acordo com o status da tarefa e some o número total de grupos

  4. Classifique a lista de tarefas em cada grupo de acordo com os requisitos de classificação da visualização ativa atual

  5. $v.任务.字段Atribuir o tipo de tarefa atual a$v.分组.字段

  6. Faça processamento adicional para componentes do gráfico de Gantt

atividade de tarefa

Encapsule a atividade da tarefa em uma função, de modo que seja conveniente chamar quando a tarefa mudar em vários locais: adicione a pessoa alterada e a hora da alteração aos parâmetros do evento recebido e insira-os no cabeçalho da matriz xtk correspondente à tarefa.

pop-up de tarefa

Ao saltar de outras páginas com um parâmetro "tarefa" ($query.task), uma caixa modal de tarefa aparecerá.

onResize

Quando o formulário não for grande o suficiente para acomodar todos os componentes, receba os seguintes componentes/visualizações em "mais". $v.项目.显示Indica o número de componentes que podem ser acomodados.

projeto

  1. lista de itens, pesquisa

  2. fixar, soltar

  3. Ver tarefas de arquivo, ativar tarefas de arquivo

  4. lápide

  5. Lista fixa

  6. Lixeira de reciclagem

  7. criar projeto

  8. Drawer
    Drawer handle.drawer-handler alternará $Vs quando clicado. O estado do menu esquerdo mudará o nome da classe draw-wrap-collapse de .drawer-wrap.

  9. Lista de componentes
    Exibe apenas um número específico: component.slice(0, $v.item.display)

  10. Mais componentes
    Component.slice($v.item.display, component.length)
    Exibe o nome do componente ao selecionar mais componentes na lista: $Vsitem.component >= $v.item.display?Component[$Vsitem.Component].Name : "Mais"

  11. Visualizações e mais visualizações

tipo de tarefa

  1. Crie um novo tipo de tarefa e modifique o tipo de tarefa

  2. Configuração de Atributos
    Atributos são campos sobre tarefas que são salvas no banco de dados.
    Atributos predefinidos: status atual, responsável, horário de início, prazo

  3. Adicionar um atributo personalizado
    "Tipo de Atributo" determina como o atributo é exibido e não pode ser alterado depois de selecionado.
    Se houver um "tipo de tarefa pai", adicione o atributo "herdado" e houver uma caixa suspensa dedicada "nome do atributo de origem" para selecionar atributos herdáveis, incluindo atributos predefinidos e atributos personalizados do tipo de tarefa pai, a herança pode ser recursivo/rastreável até a missão Ancestral de nível superior.

  4. Definir atributos personalizados
    Selecione um atributo personalizado e configure suas propriedades.
    "Nome da propriedade" é o nome do campo exibido e salvo no banco de dados. Há uma lista de opções na caixa suspensa. Ative a opção "Exibir na nova página" e ela será exibida na "Nova tarefa ", e a opção "Obrigatório" aparecerá. A opção "Obrigatório" "Preencher" adicionará um asterisco vermelho antes do nome do atributo e verificará se ele foi preenchido antes do armazenamento.

  5. Classificando e excluindo atributos personalizados

  6. Configuração de estado
    A lista de estados determina todos os estados possíveis de uma tarefa.
    Um estado tem nome, cor, tipo e comentário.
    Existem 3 tipos de status: não iniciado, em andamento e concluído, que determinam as 3 cores da barra de progresso no grupo de tarefas.
    O estado inicial é o estado padrão para tarefas recém-criadas, portanto, apenas um estado inicial é possível.

  7. Fluxo de estado O
    fluxo de estado refere-se ao estado para o qual um estado específico pode mudar. Se o fluxo de estado não for verificado ou desmarcado, um estado pode ser alterado para qualquer estado.

  8. Adicionar, editar, remover, salvar estado

 

componentes

  1. Adicionar, editar, excluir e classificar componentes
    Cada tipo de tarefa pode adicionar muitas formas diferentes de componentes
    , mas o componente de relatório não tem nada a ver com o tipo de tarefa e apenas um componente de relatório é necessário para um projeto

  2. Visualizações de Componentes
    Cada componente pode ter diversas visualizações, definindo como ele é agrupado, filtrado e classificado.
    editar, adicionar, excluir

  3. Exibição do PC
    Definir quais atributos devem ser exibidos ao exibir a lista de tarefas no lado do PC
    Adicionar, excluir e classificar os atributos a serem exibidos

  4. Kanban
    A principal característica do Kanban é que você pode arrastar e soltar grupos ou classificar.
    Ao arrastar uma tarefa para outro grupo, você precisa modificar o campo do grupo para o novo nome do grupo.
    Arrastar este grupo para classificar salvará o nome do grupo list para o "nome do grupo" na visualização Na matriz arr",
    quando a opção "arrastar e classificar" no canto inferior direito estiver ativada, a expressão no componente montado próximo a ela será acionada e a biblioteca SmoothDnD será carregado e configurado.

  5. Tabelas e listas
    são semelhantes, a diferença é que a tabela é apresentada com tabela, o nome do campo é exibido no cabeçalho da tabela, a lista é apresentada com div e o nome do campo e seu valor são exibidos juntos.

  6. Calendário
    O calendário usa o plug-in FullCalendar , que é carregado e configurado na expressão do componente de montagem.

  7. O gráfico de Gantt
    será explicado na seção seguinte

  8. Relatório
    Existe um componente de montagem que carrega o plugin highcharts e inicializa $v.任务.状态e $v.任务.任务类型.
    Cada gráfico é renderizado diretamente no respectivo componente de montagem $el, $obj.optionsuas opções dinâmicas são definidas nele e os dados dinâmicos são adicionados no evento de montagem.
    Clicar em cada quantidade exibirá uma lista de suas tarefas.

gráfico de Gantt

  1. Inicialize a função de movimento durante a montagem $v.甘特
    : cada chamada adicionará uma semana ao eixo do tempo, e quando um valor negativo for passado, o eixo do tempo será estendido para a esquerda, a cabeça do eixo - 7, o valor positivo se moverá para  o à direita, o final do eixo  + 7, no início ou Empurre a nova semana (incluindo informações de ano, mês, dia) no final da fila.
    O cálculo da data é um pouco mais complicado que o mês. Usamos o dia da semana como ponto base. A data anterior é colocada no início da fila (unshift) e a data posterior é colocada no final da fila. fila (empurrar). Vale ressaltar que a data pode ser menor que 0 ou maior que o número máximo de dias deste mês durante o processo de prorrogação, o que significa que a data abrange um mês.
    Mês: também inclui o ano, usado para renderizar o eixo ano-mês
    Dia: usado para renderizar o eixo da data
    Primeiro dia: usado para calcular o estilo esquerdo da barra de tarefas e a linha de hoje (gantt-today-line)

  2. Eixo de tempo estendido
    Quando montado, ele é pré-estendido com hoje como centro e estendido 10 semanas para frente e para trás. Ao mesmo tempo, um visualizador cruzado é colocado em ambos os lados do eixo ano-mês. Quando o usuário rola até ele , ele continuará automaticamente a se estender por 4 semanas. $v.Gantt.inited é usado para evitar o acionamento da rolagem antes da inicialização.

  3. Quando o mouse
    passa para a tarefa na coluna fixa esquerda (onMouseOver), a barra de tarefas correspondente será rolada para o intervalo visível (scrollIntoViewIfNeeded).
    Quando o mouse é movido para a barra de tarefas, ele exibe as informações de resumo da tarefa e arrasta a alça para frente e para trás.

  4. Arrastar
    o corpo da barra de tarefas e arrastar a alça para frente e para trás pode alterar o horário de início e (ou) o prazo da tarefa e, ao mesmo tempo, exibir dinamicamente o efeito visual da camada de arrastar (gantt-drag-mask).
    Quando o mouse é pressionado, o evento de movimento do mouse é iniciado a ser monitorado, e quando o mouse é liberado, o horário de início e/ou prazo são modificados de acordo com a distância/offset do movimento.
    Se não houver deslocamento ao arrastar o corpo da barra de tarefas, deverá ser considerado como uma ação de clique e uma janela pop-up exibirá os detalhes da tarefa

Tarefa

  1. Criar uma nova tarefa
    A. Há um botão "Novo" no canto superior direito dos componentes do tipo Kanban, tabela e lista para criar uma nova tarefa sem predefinição ;
    B. Cada grupo tem um botão de adição para adicionar uma nova tarefa em o mesmo grupo : No $f.创建任务evento de montagem, ele será $v.modal.分组atribuído ao campo usado para agrupamento. Se o campo for o estado, as possíveis opções de estado serão filtradas posteriormente. O mesmo acontece quando há um tipo de tarefa pai; C
    Na janela pop-up da tarefa, se permitido Se houver um tipo de subtarefa, você poderá criar uma nova subtarefa e a tarefa pai será predefinida neste momento.

  2. Proporção de progresso neste grupo de tarefas
    Há uma barra de progresso acima de cada grupo de tarefas, indicando respectivamente o número e a proporção de tarefas concluídas, em andamento e não iniciadas neste grupo.

  3. A parte superior do pop-up da tarefa
    consiste no nome do projeto, tipo de tarefa, matriz de tarefas pai, título da tarefa atual e seus respectivos ícones e cores.
    Cada tarefa pai pode ser clicada para exibir seus detalhes.

  4. compartilhar para conversar

  5. Fixada na barra de tarefas
    $v.任务栏está a lista de tarefas fixa do projeto atual, que pode ser expandida e contraída, removida e clicada para exibir detalhes.

  6. Arquivando tarefas

  7. A exclusão de uma tarefa
    também exclui uma atividade de tarefa e recursos associados a ela.
    Pesquise todos os recursos associados, clone o arr retornado e exclua-os um por um:
    $r.arr.clone().forEach('$resource.delete($x._id)')

  8. Título
    Clique para alternar para o modo de edição e focar, perder o foco ou pressione Enter para salvar as alterações e voltar ao modo de apresentação.

  9. Estado atual
    Nome de exibição, ícone e cor
    Clique para exibir uma opção de estado transferível sob o estado atual

  10. Responsável, hora de início, prazo

  11. As informações básicas
    exibem os atributos personalizados do tipo de tarefa atual e seus valores nesta tarefa.
    Eles são exibidos de acordo com seus respectivos tipos de atributos, geralmente em 3 colunas, mas rich text e texto multilinha ocupam uma única linha.

  12. A barra da guia de subtarefas e sua lista
    podem ser clicadas para exibir, desassociar, excluir tarefas, alterar o responsável e a data de vencimento

  13. Tarefas relacionadas
    Subtarefas relacionadas e indiretas

  14. Anexos
    A camada superior da janela pop-up da tarefa é um componente de montagem, que irá procurar os recursos associados. Embora a lista de anexos não seja exibida no início, o número de anexos também é exibido.
    Adicionar, renomear, excluir
    O ícone do anexo pode ser a miniatura da visualização do arquivo local antes do upload, ou a miniatura após o upload do recurso, ou o ícone do arquivo predefinido de acordo com seu tipo de arquivo.

  15. Comente

  16. As atividades
    só são exibidas quando o usuário clica, portanto o carregamento pode ser atrasado e armazenado separadamente no xtk associado, não havendo necessidade de armazená-las em tarefas como comentários.

 

selecionador de data

O seletor de data é uma função comumente usada e um pouco complicada. Poderia ter sido usado diretamente escolhendo uma biblioteca js de código aberto. No início, foi escrito diretamente para verificar a capacidade da expressão.

  1. Inicialize antes de abrir o seletor de data $v.pop:
    keep: Para evitar que a própria caixa pop-up seja clicada, consulte um evento de clique global adicionado em $c.exp.onLogin date
    : a data/hora atual é definida, se não , será
    a hora de hoje: Você precisa definir a hora, se a data acima contiver minutos/segundos, será verdade por padrão
    F5: O objetivo é acionar novamente o evento de montagem no seletor de data para reinicializar $v.日期
    cb: Selecione /clear the date é a função de retorno de chamada chamada, ela salvará/limpará a data correspondente após a verificação de "o prazo não pode ser anterior ao horário de início".

  2. O nível superior do seletor de data é um componente de montagem usado para construir $v.日期:
    Função F5: Construa a matriz de data $v.date.arr daquele mês de acordo com o ano e mês em $v.date, sempre que a montagem ou o ano e mudança de mês para ligar.
    O número de datas é maior que o número de dias de um mês, e uma matriz bidimensional de 6 semanas, ou seja, 6 * 7, é usada, portanto, algumas datas na frente são do mês anterior e outras no de volta são do próximo mês. Tomamos como ponto de partida o primeiro dia da semana deste mês, e os que são menores que ele são o mês anterior, mês-1, data + o número de dias do mês anterior; os que são maiores que o número de dias neste mês é o próximo mês, mês + 1, data- número de dias deste mês.
    Além disso, o mês 13 é janeiro do ano seguinte e o mês 0 é dezembro do ano passado.

  3. Data de retorno
    Caso seja necessário acertar a hora, deve-se clicar no botão “OK” para retornar a data construída por ano, mês, dia, hora, e clicar na data para atualizar o mês e dia em $v.data.
    Caso não precise acertar a hora e clicar na data, retornará imediatamente a data construída pelo ano, mês e dia selecionados.

  4. Limpar
    Se precisar acertar a hora, o botão "Limpar" apenas fecha o formulário de configuração de hora.
    Se não precisar acertar a hora, ele retornará vazio imediatamente para que o chamador possa limpar esta data

  5. Entrada rápida de tempo
    Além de inserir horas e minutos diretamente na caixa de entrada, você pode clicar nas setas acima ou abaixo da caixa de entrada para adicionar ou subtrair, ou até mesmo pressionar diretamente as teclas de seta para cima e para baixo no teclado para adicionar rapidamente ou subtrair. Para melhorar a eficiência, os minutos são somados e subtraídos em unidades de 5.
    Observe o tratamento especial quando horas e minutos estão fora do intervalo durante a adição e subtração.

  6. Adicionar e subtrair ano/mês
    Existem setas duplas na parte superior para adicionar e subtrair anos e ícones de seta simples para adicionar e subtrair meses. Observe que o mês pode passar para o ano passado ou para o próximo durante a verificação.

  7. Selecione rapidamente um ano/mês com um intervalo grande.
    Quando você precisa selecionar um ano distante, é um pouco lento usar as setas acima para adicionar e subtrair passo a passo. Por exemplo, para selecionar a data de nascimento de um homem idoso , clique no ano e no mês na parte superior central para selecionar diretamente o mês. Em seguida, clique no ano na parte superior central para localizar rapidamente o ano em unidades de 20 anos.

 

aprovação

Projete o formulário de aprovação, defina o processo por condição, calcule as estatísticas detalhadas e os aprovadores em tempo real, exiba o processo de aprovação, aprove/rejeite/transfira/revogue aprovação, comente e verifique registros, notifique o aprovador por mensagem instantânea e envie-o conversar.

projeto de formulário

  1. Gerenciamento de grupo

  2. gerenciamento de modelos

  3. Design de formulário de aprovação
    O designer de formulário é um editor visual de arrastar e soltar. É uma ferramenta simples de desenvolvimento de aplicativos de código zero, incluindo três partes: controles opcionais, corpo do formulário e configurações de propriedades de formulário e controle.
    Os atributos incluem: tipo de atributo, nome do atributo, texto do prompt, se é obrigatório, etc.
    Botões de opção, caixas de seleção e listas suspensas possuem listas de opções.
    Um detalhe é um contêiner de controle especial que pode conter outros controles. Os controles do tipo número nos detalhes também podem ter dois atributos: "se deve participar de estatísticas detalhadas" e "se deve ser um item de fator de processo condicional".

principal dificuldade:

  1. Arrastar e soltar usa Sortable.js .
    Os principais pontos de arrastar entre listas têm o mesmo nome de grupo (nome do grupo), e a opção de puxar grupo (puxar grupo) da lista de controle opcional usa clone, o que significa que ao ser puxado para fora da lista, uma cópia é clonada para o corpo do formulário não está sendo arrastado e o grupo não pode adicionar novos controles (coloque: false). grupo: { nome: "grupo", pull: "clone", put: false }. Configuração
    recomendada da lista aninhada : {fallbackOnBody: true, swapThreshold: 0.65}. A função onSort deve ser dividida em ordenação entre controles no corpo principal do formulário ou puxar um controle de um controle opcional. Ao puxar, há um pullMode no parâmetro que é clone (correspondente à opção pull anterior clone) .

  2. Ao arrastar um controle de uma posição para uma nova posição, primeiro remova-o da posição original (oldIndex) e depois insira-o na nova posição (newIndex)
    $fx property.splice($lenewIndex, 0, $fx property.splice ($leoldIndex, 1)[0])

  3. Configure o caminho da propriedade do controle ao clicar para selecionar um controle do formulário: $v.Approval form.Property = "Property." + $index; e o caminho da propriedade do controle nos detalhes é: $v.Approval form.Property = "Property. " + $parent.$index + ".property." + $index, eles estão aninhados no controle de detalhes, para não acionar o evento externo, use $event.stopPropagation() para evitar que o evento se propague e adicione onChange ao editar as propriedades dos
    eventos de controle, mesmo eventos vazios, irão atualizar/(renderizar) os controles correspondentes do corpo do formulário em tempo real conforme a entrada ou verificação é selecionada, para ter uma melhor experiência de edição.

  4. Existem muitos tipos de controles, e cada tipo possui diferentes formas e propriedades, por isso é necessário usar condições de renderização para controlar qual tipo de controle é exibido de acordo com os diferentes tipos. Você pode enriquecer ainda mais a lista de controle de acordo com as necessidades reais.

  5. Rerenderização
    O corpo do formulário é encapsulado com um componente montado que faz com que o formulário seja renderizado novamente quando um novo controle é adicionado ou ordenado com $v.F5属性um novo valor.
    O mesmo vale para a lista de atributos do detalhe, mas com outra variável $v.F5明细para renderizar novamente apenas os atributos do detalhe.

Defina o processo de aprovação

Diferentes tipos de aprovações, processos de aprovação e aprovadores para cada processo são diferentes

  1. Processo gratuito
    O solicitante pode escolher livremente em quantas etapas consiste a aprovação e quem precisa aprovar cada etapa. É mais flexível e adequado para aprovações com baixa complexidade e processos de aprovação incertos, mas não favorece a padronização da aprovação processo.

  2. Processo fixo
    Independentemente das condições de aplicação, o processo de aprovação é fixo, o que é adequado para aprovação com baixa complexidade, mas com processo fixo.

  3. O processo de aprovação mais comumente usado é o processo de definição de condições
    por condição. Quando as condições de aplicação são diferentes, são necessários diferentes processos de aprovação e aprovadores. Por exemplo, quando o valor do reembolso é inferior a 5.000, apenas o supervisor de primeiro nível é necessário para aprovação. Quando o valor do reembolso estiver entre 5.000 e 10.000, será necessária a aprovação do supervisor Nível 2, e a aprovação do chefe será necessária quando o número for superior a 10.000.

  4. Notificadores de Aprovação
    A aprovação não exige que alguns membros participem do processo de aprovação, mas é necessário informar o TA sobre a aprovação.

principal dificuldade:

  1. A coluna departamento é a estrutura organizacional da empresa, e um ou mais aprovadores podem ser adicionados hierarquicamente por departamento. “Chefe de Departamento” é uma variável indeterminada que será substituída por uma pessoa específica quando uma aprovação for iniciada.

  2. No processo de definição de condições, o fluxo de aprovação sob determinadas condições pode ser definido por meio de expressões, por exemplo, quanto maior o número de recrutas ou o valor do reembolso, maior será o nível de pessoas que precisam participar da aprovação. Preste atenção à continuidade das condições e não deixe lacunas.

 

iniciar aprovação

  1. Preencha o formulário de inscrição

  2. Cálculo em tempo real de estatísticas detalhadas

  3. Calcule aprovadores em tempo real
    Obtenha chefes de departamento

  4. É necessário enviar para
    validação do formulário de aprovação.
    Gerar número de aprovação

principal dificuldade:

  1. As estatísticas detalhadas são divididas em total e total.
    O total é a soma de um determinado campo a ser contado, e o total é a soma da soma de cada campo a ser contado.
    Para acumulação: reduza("$acc + $x", 0)

  2. No processo condicional, encontre o processo que atende às condições da lista de processos de aprovação de acordo com a expressão condicional pré-editada ($x.condition). Um ponto importante é que a expressão da condição de execução é passar o conteúdo do formulário de aplicação ($f. application) como ambiente de execução; outro ponto importante é encontrar a lista de aprovadores do processo, você precisa clonar os aprovadores pendentes atribuído ao formulário de inscrição, porque a próxima alteração do processo de aprovação alterará a pessoa a ser aprovada (como substituir o chefe do departamento e transferi-lo para o aprovador), e não queremos afetar o aprovador no processo original .
    $f.Application.Approvers = Aprovação Process.find('exc($x.Conditions, $f.Application)').Approvers.clone()

  3. A função “Encontrar Supervisor de Departamento” está definida na página onReady, que será executada quando o formulário de inscrição for carregado, e também será executada quando os números forem inseridos no processo condicional.
    A dificuldade é chamar recursivamente "$exp.Department Supervisor", e o departamento é pesquisado recursivamente de cima para baixo até ser interrompido por stopIf() quando chega ao departamento do candidato. Cada recursão passa o departamento atual para o ambiente de execução da expressão.

  4. O número de aprovação é a data do pedido + número de série de 4 dígitos. O número de série é um número único que começa em 1 todos os dias e é obtido aumentando ($inc) 1 no banco de dados para cada aplicação.

  5. Ao enviar para aprovação, remova o primeiro aprovador da lista de aprovadores pendentes como o aprovador atual e envie uma mensagem instantânea para o TA.

Exibição de aprovação

  1. status atual

  2. candidato

  3. Detalhes da aprovação
    Exibição de vários tipos de dados
    Detalhes e total, total

  4. Processo de aprovação

  5. informar as pessoas

  6. Comente

  7. verificar registros

principal dificuldade:

  1. O reembolso categorizado pode exibir os detalhes da aprovação de uma forma mais abrangente. Sua camada externa é um componente de dados de um elemento de tabela, que consiste em 4 tcorpos, mas controla o segundo componente de dados usado para exibir detalhes. O primeiro tbody está vazio. O terceiro um é usado para exibir a lista de atributos e o quarto é colocado na parte inferior para exibir o total.
    Vale ponderar que o segundo componente de dados utilizado para exibir os detalhes não está configurado com elementos HTML, ou seja, não será renderizado como um elemento HTML em si, mas sim para controlar a renderização de seu primeiro elemento filho, ou seja, para exibir a lista de detalhes em um loop (p. Os dois subelementos são usados ​​para exibir o total), e cada detalhe tem vários atributos, e o componente de dados é usado novamente, e há atributos de matriz no detalhe para usar o componente de dados (como upload de vários anexos).
    Este é um exemplo de aninhamento de componentes de dados para mostrar dados complexos ao extremo.

  2. O componente de dados no processo de aprovação controla o segundo li, que é obtido pela fusão do fluxo de aprovação, o aprovador atual e a pessoa a ser aprovada. O primeiro li é fixado ao solicitante e não é controlado pelo componente de dados.
    O [fluxo de aprovação] aqui se refere às pessoas que participaram da ação de aprovação, que podem incluir a pessoa revogadora (o próprio solicitante) e o cessionário. Cada ação envia informações relevantes para o fluxo de aprovação: $push: { "x. fluxo de aprovação": { quem: $c.me._id, hora: data(), ação, opinião} }

  3. A entrada do comentário não é usada para entrada, mas quando ele é clicado (deixe $v.modal.comment = true quando onFocus) um formulário de comentário com área de texto aparece, e o formulário de comentário pode ser retraído clicando em outro lugar porque é em um local de nível superior (modal -detail-main) Deixe $v.modal.comments vazio.

  4. Há um componente de montagem na janela modal de aprovação. Cada vez que um não solicitante abre a janela modal de aprovação, um registro de revisão será adicionado a [Revisão de Aprovação].

  5. Para reduzir a quantidade de informações de aprovação, tanto os comentários quanto as referências de aprovação são armazenados na tabela xtk associada, com aprovado._id como chave. Comentários e revisão de aprovação não são lidos em vários cenários de lista de aprovação.Quando a janela modal de aprovação é aberta,os comentários são lidos por padrão no componente de montagem acima,mas a revisão de aprovação ainda não é lida.

Processo de aprovação

  1. Concordo em aprovar

  2. Recusar-se a aprovar

  3. Encaminhar para aprovação

  4. aprovação revogada

  5. Aprovação de comentários

  6. Ver aprovação

notificação de aprovação

  1. Eu me candidatei

  2. Eu aprovei

  3. me informe

  4. notificação de mensagem instantânea

  5. enviar para bate-papo

principal dificuldade:

  1. Obtenha os dados mais recentes/re-renderize
    Movimentos pendentes entre diferentes pessoas e as informações mudam rapidamente. Além de querer notificar "I" sobre aplicativos que aguardam a aprovação de "I" por mensagem instantânea, também espero receber aplicativos [pendentes de aprovação] relacionados a "I" quando estiver na página de aprovação.
    Número de aprovações pendentes: Existe um componente de montagem no nó raiz, que é usado como $v.F5expressão chave. Sempre que $v.F5 muda (enviar, revogar, concordar, rejeitar e transferir para aprovação), a aprovação pendente relacionada a " I" será puxado novamente Quantidade
    Lista de aprovação: A tabela da lista de aprovação é empacotada com um componente de montagem $v.tab + $v.F6para a expressão chave. Ao alternar na barra de navegação esquerda (aplicado, aprovado, notificado) ou na barra de abas no canto superior direito (aprovação pendente, aprovado), a lista de aprovações mais recentes será puxada novamente.

Acho que você gosta

Origin blog.csdn.net/weixin_52095264/article/details/126093458
Recomendado
Clasificación