La herramienta de colaboración de proyectos de alta imitación [Worktile] lo lleva paso a paso desde cero para realizar funciones como estructura organizacional, disco de red, mensaje, proyecto y aprobación.

declaración

Este curso fue una imitación de Worktile desarrollado por Beijing Yicheng Starlight Technology el 6 de agosto de 2020. Los derechos de autor de los materiales relevantes pertenecen al sitio web original. Este curso es un trabajo de estación de imitación, solo para fines didácticos y no comerciales. Para cooperación comercial , visite el sitio de origen.

Antes del desarrollo del curso, probé casi todas las plataformas de oficina colaborativa empresarial del mercado. Elegí Worktile porque pensé que era la mejor en ese momento y también me gustó su estilo de interfaz de usuario. Se lo recomendaría a mis amigos que lo necesitaran.

El propósito de este curso es demostrar un complejo software de aplicación SaaS colaborativo similar a OA, basado en ZhongTouch, una plataforma de aplicaciones profesional de bajo código, cómo desarrollar una aplicación SaaS compleja con funciones completas. Worktile es un sistema de gestión de colaboración en proyectos especialmente diseñado para escenarios de oficinas empresariales, con muchos conceptos de negocio y relaciones intrincadas; aunque probablemente no necesites hacer este tipo de aplicación compleja, es muy valioso aprender las ideas y usos que se utilizan en ella. .

El sitio de origen puede evolucionar de forma iterativa con el tiempo, pero los trabajos del curso no cambiarán en consecuencia, por lo que es normal que los trabajos sean diferentes del sitio de origen que ve ahora.


Este curso cuenta con un vídeo muy detallado que explica cada módulo funcional, que se implementa paso a paso desde una página en blanco: descripción general , estructura organizativa , disco de red , mensajería instantánea , gestión de proyectos y aprobación .

cuenta

Para demostrar la estructura organizacional de la empresa, las siguientes personas son ficticias:

  1. Director ejecutivo: Dai Guoqiang 13845678901 Administrador

  2. Gerente General Gerente de Oficina: Zhao Min 13845678911 Administrador

  3. Director de personal: Chen Ying 13845678902 administrador

  4. Director financiero: Fu Yuan 13845678906 Administrador

  5. Líder del equipo del proyecto: Zeng Hui 13845678312

  6. Desarrollo del equipo del proyecto: Huang Hongliang 13845678909

  7. Desarrollo del equipo del proyecto: Huang Yaoqian 13845678223

  8. Director técnico: Xie Linhua 13845678907

  9. Gerente del equipo del proyecto: Sun Ronghao 13845678905

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

  11. Líder del equipo de prueba: Wang Boxiang 13845678908

  12. Departamento no asignado: Huang Feikai 13845678102

Estos personajes y teléfonos móviles están todos inventados y los avatares son de mis notas .

Esta aplicación del curso se utiliza como plantilla para que los estudiantes la clonen, los datos no deben ser demasiado confusos, no se proporciona una cuenta de demostración y el registro no está abierto, por lo que incluso si esta aplicación se clona, ​​no se puede iniciar sesión directamente. Después de la clonación, vaya a la página de inicio de sesión (/z/login), que utiliza el complemento de administración de inicio de sesión de la cuenta, haga clic derecho para seleccionar el complemento, active "Registrarse" en el panel derecho, guarde y actualice la página para registrarse. Después del registro, tiene una cuenta para iniciar sesión, pero todavía no hay una contraseña para iniciar sesión con las cuentas del personal anterior. Primero puede comentar las restricciones en $user.toggleRole en "Seguridad de backend" y luego hacer clic. "Configuración" de un miembro en "Administración de miembros" y haga clic en "Modificar". Después de modificar la contraseña de este miembro en la pestaña "Cuenta de miembro", la contraseña iniciará sesión como esta persona. Para obtener orientación detallada, vaya al vídeo de enseñanza.

Los estudiantes que estén listos para una investigación en profundidad, regístrese e inicie sesión en el sitio web oficial de ZhongTouch , haga clic en el botón clonar y copie la aplicación completa para depurarla y modificarla a voluntad.

V$ globales

El estado persistente global sson las iniciales de status . Lea desde el almacenamiento local cuando se inicia la aplicación y guárdelo nuevamente en el almacenamiento local cuando finalice la aplicación (antes de descargar), para que pueda restaurar inmediatamente el estado en el que lo dejó la última vez.

  1. $Vs navegación izquierda: la última etiqueta de navegación izquierda

  2. Mensaje $Vs: el _id del último contacto o grupo de chat privado

  3. $Vs no leídos: el número de mensajes no leídos más recientes en cada chat o grupo privado

  4. $Vs leídos: la hora del último mensaje leído de cada chat o grupo privado

  5. $Vs colapsado: varios menús colapsados

  6. Proyecto $Vs: el último proyecto abierto _id y su subíndice de componente y subíndice de vista

  7. Barra de tareas $Vs: lista de tareas ancladas a la barra de tareas por proyecto

evento global

  1. hacer clic: Cuando haya una ventana emergente, $v.popciérrela, pero keepsi es cierto, haga clic dentro de la ventana emergente o fuera de la página z (confirmar o alertar) para no cerrarla.

  2. Keydown: cuando haya una ventana emergente o ventana modal, presione la tecla Salir Escapepara cerrar la ventana emergente o ventana modal.

Organización

La estructura organizativa de una empresa es la base de otras funciones. Puede administrar varios departamentos y sus miembros en una estructura de árbol, y también puede verificar el estado de las tareas que están manejando sus colegas desde la libreta de direcciones.

  1. La lista de trabajos
    se puede agregar, modificar, subir, bajar y eliminar.

  2. Cuando el usuario inicia sesión ($c.exp.onLogin), se extrae la información de todos los empleados de la empresa y la estructura organizativa de la empresa, es decir, el departamento $c.exp.

  3. La estructura organizativa y de empleados está integrada en variables globales, lo que resulta conveniente para su uso en muchos lugares de la aplicación.
    $V.部门Es un clon de la estructura organizacional, excepto por la adición de un "departamento no asignado".
    $V.部门成员Después de aplanar la estructura organizacional, agregar miembros de cada departamento
    $V.部门总成员se basa en los $V. miembros del departamento y luego agregar todos los miembros descendientes del departamento.

  4. Gestión de miembros
    Configuración de información básica, modificación de cuenta de miembro, configuración de roles

  5. Ajustar el departamento
    Puede seleccionar miembros (uno o todos) para ajustar sus departamentos en lotes

  6. Búsqueda de miembros (filtro)

dificultad principal:

  1. Mover es intercambiar posiciones. Las funciones nativas se usan comúnmente. arr.splice(新下标, 0, arr.splice(旧下标, 1)[0])Primero, elimine un elemento del subíndice anterior, y el elemento eliminado es una matriz, así que agregue el [0]elemento en sí más tarde y luego inserte el elemento eliminado en el nuevo subíndice.
    También es fácil entender que $l.arr.splice($index - 1, 0, $l.arr.splice($index, 1)[0]) está subiendo, $l.arr.splice($index + 1, 0, $l.arr.splice($index, 1)[0]) se mueve hacia abajo.
    Después de aprender los siguientes cursos y dominar cómo usar Sortable.js , cambiar los botones arriba y abajo aquí para ordenar por arrastre hará que la operación sea más fácil e intuitiva.

  2. Para eliminar un elemento de una matriz en la base de datos , comúnmente se usa $pull , es decir, para extraer un elemento de la matriz :
    $xtk.modify("Compañía", "Posición", {$pull: {"x.arr ":$x} })

  3. Estructura de datos
    Los departamentos se almacenan en una estructura de árbol, el departamento tiene un nombre y un supervisor, y la matriz zchildren se usa para almacenar subdepartamentos.
    Cuando la fuente de datos del componente de datos contiene zchildren, se representará de forma recursiva en una estructura de árbol.

  4. Sangría de subdepartamento
    Para reflejar la jerarquía departamental, la sangría izquierda (paddingLeft) se calcula de acuerdo con la profundidad jerárquica ($indexes) en el estilo dinámico

  5. $v.F5Vuelva a renderizar permitiendo que el componente de montaje obtenga los datos más recientes después del ajuste del departamento o la actualización de los miembros.

  6. El departamento que utiliza para eliminar miembros es $unset. Esté atento al departamento especial "departamento no asignado".

  7. Para eliminar subdepartamentos $pull, "aléjese" de la matriz.

 

directorio

  1. lista de colegas

  2. Detalles del colega

  3. Lista de tareas de colega y número de estado

  4. enviar un mensaje

  5. Contactos frecuentes

  6. recolectar

  7. Buscar compañeros por departamento

disco de red

Al igual que Baidu Netdisk y Alibaba Netdisk, administra archivos almacenados en la nube en forma de carpetas virtuales y carga carpetas enteras en lotes.

Visualización del disco de red

  1. Leer información de carpetas y archivos.

  2. árbol de carpetas

  3. ruta de archivo

  4. buscar

  5. para ordenar

dificultad principal:

  1. Estructura de datos de árbol Las
    carpetas son solo registros simples almacenados en la tabla de productos, que contienen 名称información 颜色( es decir, la carpeta principal), en lugar de una estructura de árbol directa, pero necesitamos construir una estructura similar a la lección anterior basada en " parent " fuente de datos del componente de datos .
    Las carpetas pueden tener una estructura jerárquica muy profunda, que no es tan única y estable como la estructura organizativa, y no se pueden leer todas las carpetas a la vez. En onReady, primero leemos el archivo de nivel superior y la carpeta de nivel superior (su carpeta principal es "ninguna") y leemos las subcarpetas directas respectivas a su vez (dependiendo de si hay subcarpetas para determinar si mostrar la opción "expandir" icono de triángulo pequeño).
    Un punto importante para leer un archivo o carpeta es pasar la carpeta principal _id al entorno de ejecución: { 父: _id }.
    Un archivo es un recurso ordinario cargado en el servidor de almacenamiento de objetos y no tiene información de árbol de estructura de archivos, por lo que debemos agregar la carpeta actual como carpeta principal a la tabla de recursos cada vez que cargamos un archivo: $resource.modify (_id, {
    "x.type": "disco de red", "x.parent": carpeta_id actual })

  2. Varias variables clave
    $v.根son la raíz del árbol de estructura de carpetas (el nombre se llama "Disco de red", el primer nodo de la ruta del archivo), que es el árbol más grande. Sus "zchildren" contienen las carpetas de nivel superior leídas anteriormente y "files" contiene los archivos de nivel superior. En particular, su _id es "ninguno".
    $v.树Es un árbol grande y pequeño que comienza desde todos los niveles de carpetas con la carpeta principal _id como clave. Entonces $v.树.无eso es $v.根.
    $v.路径es la matriz de árboles de carpetas en todos los niveles desde la raíz del árbol hasta la
    $v.文件夹carpeta actual es el id_carpeta actual. Cuando el usuario hace clic (ya sea en el árbol de estructura de archivos a la izquierda o en la lista de carpetas principales), la usará como carpeta principal para leer la carpeta y la información del archivo que contiene. Además, al hacer clic en el ícono del triángulo pequeño "expandir", solo necesita leer y escribir la información de la carpeta secundaria, por un lado, la información del archivo no se muestra en el árbol y, por otro lado, la Se ha leído la subcarpeta. La información de la carpeta secundaria es para determinar si se muestra la subcarpeta. Triángulo pequeño para carpetas.


  3. Color dinámico de carpeta (componente personalizado)

  4. Volver a renderizar Hay tres componentes montados que volverán a ensamblar los datos y los renderizarán nuevamente cuando se modifiquen
    . A. El componente de montaje en el árbol de estructura de archivos de la izquierda se usa para ensamblar , comenzando desde el nodo raíz y atravesando recursivamente todas las carpetas buscadas: por un lado, monte la carpeta directamente, por otro lado, empújela en el Matriz zchildren de la carpeta principal. B. El componente de montaje en la ruta del archivo en la parte superior se usa para ensamblar , de forma recursiva desde la carpeta actual hasta el nodo raíz, y empuja cada capa de carpetas hacia el encabezado de la matriz de ruta (sin desplazamiento). C. El componente de montaje en la lista de archivos principal se utiliza para ensamblar la lista de archivos en la carpeta actual.$v.F5
    $v.树$v.树
    $v.路径

  5. Buscar y ordenar
    Siempre mostramos primero la lista de carpetas y luego la lista de archivos, que se buscan y clasifican por separado, y luego las dos se unen: (
    $v.search.folder|| $v.tree[$v . carpeta].zchildren || []). sort ($v.sort.key, $v.sort.incr).concat ( ($v.search.file || $v.tree[$v.folder] . archivo|| []). ordenar ($v.sort.key, $v.sort.incr))

 

Carga de disco de red

  1. nueva carpeta

  2. Cargar archivos (múltiples opciones)

  3. Cargar carpeta (mantiene la estructura de archivos)

  4. Subir lista, subir progreso y estado

dificultad principal:

  1. El botón cargar archivo activa
    el elemento de entrada oculto: $el.firstElementChild.click().
    Después de que la carga sea exitosa, $l.Use configura al comienzo de la selección de archivos (onChange) para evitar que el usuario haga clic en otras carpetas durante el proceso de carga, lo que hará que la carpeta $v. cambie y actualice el archivo actual del usuario. List ($v.exp.file.exc({parent: $v.folder})) no tiene esta preocupación.

  2. Cargar carpeta
    Para evitar verse afectado por el cambio de la carpeta $v., primero asígnela a una variable temporal: $l.carpeta = $v.carpeta. La clave para cargar recursivamente toda la carpeta es que cada archivo que se va a
    cargar tiene una ruta relativa webkitRelativePath. Recorra cada nodo de ruta, cree nodos de carpeta no creados, hasta que la carpeta más interna se asigne a $l. la carpeta actual y utilícela como carpeta principal una vez completada la carga.

  3. Lista de carga
    Inserte la información en $v.上传la matriz antes de cargar el archivo, marcado como "cargando", si es una imagen o un video, cree una miniatura para él: URL.createObjectURL (archivo), cambie su estilo de progreso a tiempo durante la carga proceso, una vez completada la carga Elimine el indicador "cargando".

Gestión de discos de red

  1. doble denominación

  2. mover

  3. borrar

  4. modificar color

  5. enviar al chat

  6. papelera de reciclaje

dificultad principal:

  1. Eliminamos una carpeta o archivo para sacarlo de su ubicación original, pero luego hay una operación de restauración, por lo que se debe conservar la información en la ubicación original. Implementamos $rename: {"x.parent": "x.original parent"}
    cambiando la carpeta principal a la carpeta principal original, de modo que los recursos sin carpetas principales se eliminen lógicamente. "x.Padre original": {$existe: verdadero}


mensaje instantáneo

Similar a la mensajería instantánea corporativa WeChat/Feishu/DingTalk, admite chat privado, chat grupal, mensajes históricos, fijación en la parte superior, puede enviar emoticonos, imágenes, archivos de disco de red, proyectos y aprobaciones de conversaciones, y también puede marcar archivos como favoritos y fijos. mensajes. Es un gran curso para aprender sobre conexiones.

inicialización de mensajes

  1. Obtenga la información de la lista de grupos privados (los miembros del grupo me incluyen a mí) y grupos públicos, y haga una lista de sus _ids $V.消息.群ID.

  2. Abra la conexión $socket.open():
    lista group_id como channels, para que pueda recibir mensajes de grupos relacionados;
    saveToDBconfigúrelo en verdadero, guarde la información en la base de datos, para que pueda ver el historial de mensajes a través de $socket.hist(), incluso si no está en línea. lo envía otra persona. Los mensajes no leídos de también se pueden recibir;
    allowMultiLoginconfigúrelo en verdadero, para que varios dispositivos/navegadores abiertos puedan recibir mensajes al mismo tiempo.

  3. onConnect se ha conectado
    obteniendo $socket.hist()todos los mensajes del historial de chats privados.
    Obteniendo $socket.hist($V.消息.群ID)mensajes históricos de todos los grupos públicos y privados.
    Agregue la fecha del mensaje enviado a cada mensaje y muestre la fecha y la semana para la división en la lista de mensajes para su comodidad.
    Obtenga una lista de _id de las sesiones fijas.
    Según el objeto de chat privado _id o chat grupal _id:
    coloque cada lista de mensajes en $V.消息.chat;
    coloque cada lista de mensajes adicional en $V.消息.more, y cada vez que la ventana de chat se desplace hacia la parte superior, saque un _id para obtener mensajes históricos anteriores;
    coloque cada mensaje _id en $V.消息.histIDél y utilícelo al eliminar mensajes históricos; coloque el número de mensajes
    cuyo tiempo de envío de cada mensaje sea mayor que su tiempo de lectura ;$V.s.已读$V.s.未读

  4. onData recibe un nuevo mensaje
    y lo coloca en la lista de mensajes correspondiente según el objeto de chat (objeto_id de chat privado o id_de chat grupal) $V.消息.chat.
    Si el mensaje está en la sesión de chat abierta actualmente, el nuevo mensaje se mostrará en la vista después de renderizarlo; de lo contrario, la cantidad de mensajes no leídos aumentará en 1.
    Si este mensaje contiene información de @my, aparecerá una ventana de notificación (si no está bloqueada).

sesión de mensajes

  1. Crear grupo/configuraciones de grupo

  2. unirse al grupo

  3. Iniciar un chat privado

  4. lista de conversaciones. Independientemente de si hay mensajes en la sesión superior, todos los mensajes se muestran primero y luego se muestra la sesión con mensajes excepto el superior:
    $V.message.top.concat($V.message.chat.keys() .filter('!$V.message .top.includes($x)'))

  5. Filtrado de sesiones:
    .filter('$f.search.kw ? ($c.user[$x].x.name|| $c.xdb[$x].x.name).includes($f.search. kilovatios): 1')

  6. Mensajes no leídos, incluidos mensajes históricos no leídos y mensajes nuevos instantáneos no leídos.
    Total no leído: $Vs unread.values().reduce('$acc + $x', 0)

  7. Al hacer clic en la sesión
    , asigne el ID de sesión al mensaje $Vs, muestre la lista de mensajes y desplácese hasta el último mensaje.
    Si el número de mensajes no leídos es inferior a 6, configure todos los mensajes como leídos (borre los no leídos y registre la hora de envío). del último mensaje)

  8. eliminar sesión
    A. eliminar la lista de archivos de la sesión asociada
    B. eliminar el archivo en sí en la lista de archivos
    C. eliminar la lista de mensajes anclados de la sesión asociada
    D. eliminar todo el historial de mensajes.

mensaje instantáneo

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

  2. Mostrar fecha dividida cuando la fecha no es la misma que la del mensaje anterior.
    fecha!== $matriz[$índice - 1].fecha

  3. Alinee el mensaje enviado por usted mismo a la derecha para distinguirlo del mensaje enviado por otros
    $c.me._id === from ? " message-item--me": ""

  4. Cuadro emergente de mensaje
    Mensaje fijo, elimina el mensaje enviado por ti mismo

  5. HTML组件Representar el cuerpo del mensaje con

  6. Observador cruzado
    Si hay más mensajes para leer ($V.message.more[$Vsmessage].length), al desplazarse hasta la parte superior del cuadro de mensaje, el observador cruzado tomará un elemento y lo entregará. $V.消息.moresobre $socket.more() para obtener más mensajes y colocarlos al principio de la lista de mensajes, desplácese hasta el primer mensaje después de renderizar, pero el visor cruzado está fuera de la ventana, para evitar una activación continua, pero espere a que usuario para ir más lejos Se activará nuevamente al desplazarse hacia arriba.
    Un punto de atención es introducir una variable temporal $vlmore para evitar la primera ejecución causada por cada nueva representación del visor cruzado, porque no es causada por el usuario que se desplaza activamente hacia arriba en este momento.

  7. Mensajes no leídos
    Muestra la cantidad de mensajes no leídos; al hacer clic, desplácese hasta el último mensaje leído o la parte superior del cuadro de mensaje para activar un observador cruzado para ver más mensajes y luego activar "marcar todo como leído".

  8. Envía un mensaje de texto
    y [表情文本]reemplázalo con la img correspondiente: .replace($c.reg.emoticon, $c.fun.emoticon);
    @alguien: Cuando se presiona @ en una conversación grupal (es decir, Shift + 2, el keyCode of 2 Es 50) Aparecerá una lista de miembros del grupo excluyéndose a uno mismo, pero cuando se abra la ventana, presione una tecla que no sea Shift (keyCode es 16) para cerrar la ventana emergente; reemplácela con el nombre
    correspondiente @24位的某人_id: .replace($c.reg.mention, $c.fun.mention); hay 成员una ventana emergente al hacer clic porque se agrega el evento onClick: window.mention click();
    $socket.send($Vs message, "texto", $f.mensaje.txt);

  9. Enviar mensaje de imagen/archivo
    Haga clic en el icono de archivo para que aparezca un cuadro de diálogo de carga, seleccione uno o más archivos y cárguelos en el servidor,
    luego conviértalos en los textos HTML correspondientes según los diferentes tipos de archivos: ipara imágenes, muestre directamente miniaturas de imágenes; vpara videos, muestre la captura de pantalla del video correspondiente; fpara otros tipos de archivos, se mostrarán diferentes íconos según el formato del sufijo del archivo);
    después de enviar el mensaje, la información del archivo se agregará a la lista de archivos de esta sesión.
    Vale la pena señalar que la base de datos no se opera inmediatamente después de enviar el mensaje, sino que el contenido se coloca en la variable $v para guardarlo en el archivo del mensaje y luego se ejecuta en $c.exp.onData después de que regresa el mensaje. , porque se considera que cuando el archivo se elimine en el futuro También se debe eliminar del historial de mensajes, al eliminar se utiliza el tiempo de envío del mensaje d: $socket.pull(_id, from, d), y esta vez Se genera en el lado del servidor y solo se conoce después de recibir el mensaje.

  10. Enviar mensaje de tarea/aprobación/disco de red del proyecto

otro

  1. Lista de archivos Los
    archivos enviados desde el disco de red al chat no están en esta lista.
    Para eliminar un archivo:
    A. Eliminar el archivo en sí
    B. Eliminar de la lista de archivos de la sesión asociada
    C. Eliminar el mensaje correspondiente del historial de mensajes .

  2. Lista de mensajes fijos
    Eliminado de la lista de mensajes fijos de la conversación asociada cuando se elimina

  3. Haga clic en el ícono de miembro
    para mostrar los miembros del grupo en el grupo, puede mostrar aún más la información del miembro o eliminar al miembro.
    Mostrar la información del miembro en el chat privado

  4. Haga clic en el ícono de configuración
    Anclar y desanclar conversaciones
    Configuración del grupo
    Dejar un grupo
    Eliminar un grupo

gestión de proyectos

La gestión de la colaboración en proyectos es la función principal de Worktile, que se utiliza para planificar, organizar y realizar un seguimiento de algunas tareas interrelacionadas con un propósito claro. Admite configuraciones personalizadas altamente flexibles para adaptarse a las necesidades del proyecto en diversos escenarios.

Worktile planifica, controla y realiza un seguimiento de tareas en diversas áreas y escenarios de trabajo de forma basada en proyectos.

Conceptos de gestión de proyectos

proyecto

El centro de recopilación de tareas se utiliza para planificar, organizar y dar seguimiento a algunas tareas interrelacionadas con un propósito claro. Los proyectos pueden ser de corto o largo plazo.

componentes

El método de visualización de tareas presenta la información de la tarea a los usuarios en diferentes formas visuales. Hay varios tipos de componentes: kanban, lista, tabla, diagrama de Gantt, calendario e informe.

vista

Los componentes pueden tener varias vistas para agrupar, filtrar y ordenar tareas.

tipo de tarea

El modelo que incluye diferentes escenarios comerciales se compone de atributos de tarea altamente estructurados, que pueden especificar la relación entre los tipos de tareas y el modo de flujo de estado, formando así una especificación para el almacenamiento de información y la colaboración en equipo.

Atributo : El tipo de tarea puede especificar qué información personalizada puede contener este tipo de tarea. Hay varios tipos de atributos: texto, texto enriquecido, número, fecha, miembro, miembros múltiples, selección única desplegable, selección múltiple desplegable, herencia. .

Tarea

Una instancia de un tipo de tarea. Cada tarea tiene un título, estado, responsable, participantes, hora de inicio y finalización, comentarios, etc. La parte principal son los distintos atributos definidos en el tipo de tarea.

Se recomienda que intente expandir más tipos de atributos después de clonar este curso, e incluso expandir los tipos de componentes.

modelo de datos

proyecto:

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

Tipo de tarea:

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

Tarea:

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

Entre ellos, el atributo de tarea es almacenar los datos específicos especificados por el atributo del tipo de tarea al que pertenece, como el atributo de la tarea de reclutamiento:

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

Instale el complemento de administración de bases de datos para una comprensión más intuitiva del modelo de datos.

inicialización

cargar proyecto

  1. Cargar proyectos privados en los que contribuyo

  2. cargar proyecto publico

  3. Combine los dos elementos anteriores y extraiga información breve para formar $v.项目.菜单una lista.

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

  5. Si lo hay $V.s.项目._id, continúa cargando la información de este proyecto.

  6. El tipo de tarea para cargar este elemento.

  7. Cargar todas las tareas para este proyecto.

  8. 任务栏la matriz para restaurar este elemento

clasificación de tareas

Organice los datos sin procesar cargados arriba en $v.任务.

  1. Si hay un tipo de tarea principal especificado en el tipo de tarea, agréguelo a su $v.任务.子任务类型tipo de tarea principal

  2. Asigne los atributos personalizados en cada tipo de tarea $v.任务.属性a la lista correspondiente

  3. Para facilitar la lectura, continúe asignando la lista de atributos anterior al $v.任务.字段objeto correspondiente de acuerdo con los campos que contiene.

  4. Sin cambiar la constante $c original, clone todos los datos de la tarea y asígnelos a la matriz, y forme objetos $v.任务.arrde acuerdo con su _id$v.任务.O

  5. Si una tarea tiene una tarea principal, agregue la tarea a la matriz de subtarea de su tarea principal de acuerdo con el tipo de tarea x.子任务y busque recursivamente la cadena de tareas principal para formar una matriz de cadena de tareas x.父任务arr, que se muestra como una barra de navegación en la parte superior. parte superior de la ventana emergente de tareas

  6. Para el atributo del tipo de herencia definido en el tipo de tarea, agregue un campo de herencia a este atributo en todas las tareas debajo de él.

Agrupamiento

Organice los datos sin procesar cargados arriba en$v.分组

  1. Asigne el componente del proyecto actualmente activo (es decir, $Vs proyecto. componente) a$v.分组.组件

  2. Después de que las tareas bajo el tipo de tarea del componente activo actual se filtran según los requisitos de filtrado de la vista activa actual, se agrupan de acuerdo con el campo de agrupación especificado por la vista activa actual, y cada tarea se agrega a la matriz, y diferentes los atributos de agrupación forman $v.分组.O[分组属性]una $v.分组.arrmatriz

  3. Cuente el número de tareas de cada grupo según el estado de la tarea y sume el número total de grupos

  4. Ordene la lista de tareas en cada grupo de acuerdo con los requisitos de clasificación de la vista activa actual

  5. $v.任务.字段Asigne el tipo de tarea actual a$v.分组.字段

  6. Realizar procesamiento adicional para los componentes del diagrama de Gantt

actividad de tarea

Encapsule la actividad de la tarea en una función, de modo que sea conveniente llamarla cuando la tarea cambie en varios lugares: agregue la persona de cambio y la hora de cambio a los parámetros del evento entrante e insértelos en el encabezado de la matriz xtk correspondiente a la tarea.

ventana emergente de tarea

Al saltar desde otras páginas con un parámetro de "tarea" ($query.task), aparecerá un cuadro modal de tarea.

en Cambiar tamaño

Cuando el formulario no sea lo suficientemente grande para acomodar todos los componentes, reciba los siguientes componentes/vistas en "más". $v.项目.显示Indica el número de componentes que se pueden acomodar.

proyecto

  1. lista de artículos, búsqueda

  2. fijar, desanclar

  3. Ver tareas de archivo, activar tareas de archivo

  4. lápida sepulcral

  5. Lista fija

  6. papelera de reciclaje

  7. crear proyecto

  8. Cajón
    Manija del cajón.drawer-handler alternará $Vs cuando se haga clic. El estado del menú de la izquierda cambiará el nombre de la clase cajón-wrap-collapse de .drawer-wrap.

  9. Lista de componentes
    Solo muestra un número específico: componente.slice(0, $v.item.display)

  10. Más componentes
    Component.slice($v.item.display, componente.length)
    Muestra el nombre del componente al seleccionar más componentes en la lista: $Vsitem.component >= $v.item.display? Component[$Vsitem. Component].Nombre : "Más"

  11. Vistas y más vistas

tipo de tarea

  1. Crear nuevo tipo de tarea y modificar tipo de tarea

  2. Configuración de atributos
    Los atributos son campos sobre tareas que se guardan en la base de datos.
    Atributos predefinidos: estado actual, persona responsable, hora de inicio, fecha límite

  3. Agregar un atributo personalizado
    "Tipo de atributo" determina cómo se muestra el atributo y no se puede cambiar una vez seleccionado.
    Si hay un "tipo de tarea principal", agregue el atributo "heredado" y hay un cuadro desplegable dedicado "nombre de atributo de origen" para seleccionar atributos heredables, incluidos los atributos predefinidos y atributos personalizados del tipo de tarea principal, la herencia puede ser recursivo/rastreable hasta la misión ancestral de nivel superior.

  4. Definir atributos personalizados
    Seleccione un atributo personalizado y configure sus propiedades.
    "Nombre de propiedad" es el nombre del campo que se muestra y se guarda en la base de datos. Hay una lista de opciones en el cuadro desplegable. Active el interruptor "Mostrar en nueva página" y se mostrará en la ventana "Nueva tarea". " ventana emergente y aparecerá la opción "Requerido". El interruptor "Rellenar" "Requerido" agregará un asterisco rojo antes del nombre del atributo y verificará si se ha completado antes del almacenamiento.

  5. Ordenar y eliminar atributos personalizados

  6. Configuración de estados
    La lista de estados determina todos los estados posibles de una tarea.
    Un estado tiene un nombre, color, tipo y comentario.
    Hay 3 tipos de estados: no iniciado, en progreso y completado, que determinan los 3 colores de la barra de progreso en el grupo de tareas.
    El estado inicial es el estado predeterminado para las tareas recién creadas, por lo que solo es posible un estado inicial.

  7. Flujo de estado El
    flujo de estado se refiere a qué estado puede cambiar un estado específico. Si el flujo de estado no se verifica o borra, un estado se puede cambiar a cualquier estado.

  8. Agregar, editar, eliminar, guardar estado

 

componentes

  1. Agregar, editar, eliminar y ordenar componentes
    Cada tipo de tarea puede agregar muchas formas diferentes de componentes
    , pero el componente de informe no tiene nada que ver con el tipo de tarea y solo se requiere un componente de informe para un proyecto

  2. Vistas de componentes
    Cada componente puede tener varias vistas, definiendo cómo se agrupa, filtra y ordena.
    editar, agregar, eliminar

  3. Pantalla de PC
    Defina qué atributos se deben mostrar al mostrar la lista de tareas en el lado de la PC
    Agregue, elimine y ordene los atributos que se mostrarán

  4. Kanban
    La característica principal de Kanban es que puede arrastrar y soltar grupos u ordenar.
    Al arrastrar una tarea a otro grupo, debe modificar el campo del grupo al nuevo nombre del grupo.
    Arrastrar este grupo para ordenar guardará el nombre del grupo. liste al "nombre de grupo" en la vista En la matriz arr",
    cuando el interruptor "arrastrar y ordenar" en la esquina inferior derecha está activado, se activará la expresión en el componente montado al lado y la biblioteca SmoothDnD será cargado y configurado.

  5. Las tablas y las listas
    son similares, la diferencia es que la tabla se presenta con tabla, el nombre del campo se muestra en el encabezado de la tabla, la lista se presenta con div y el nombre del campo y su valor se muestran juntos.

  6. Calendario
    El calendario utiliza el complemento FullCalendar , que se carga y configura en la expresión del componente de montaje.

  7. El diagrama de Gantt
    se explicará en la siguiente sección.

  8. Informe
    Hay un componente de montaje que carga el complemento Highcharts e inicializa $v.任务.状态y $v.任务.任务类型.
    Cada gráfico se representa directamente en el componente de montaje respectivo $el, $obj.optionsus opciones dinámicas se definen en él y los datos dinámicos se agregan en el evento de montaje.
    Al hacer clic en cada cantidad, aparecerá una lista de sus tareas.

Gráfico de gantt

  1. Inicialice la función de movimiento al montar $v.甘特
    : cada llamada agregará una semana al eje de tiempo, y cuando se pase un valor negativo, el eje de tiempo se extenderá hacia la izquierda, el cabezal del eje - 7, el valor positivo se moverá hacia  la a la derecha, el extremo del eje  + 7, al principio o Empuje la nueva semana (incluida la información del año, mes y día) al final de la cola.
    El cálculo de la fecha es un poco más complicado que el mes. Usamos el día de la semana como punto base. La fecha anterior se empuja al principio de la cola (unshift) y la fecha posterior se empuja al final de la cola. cola (empujar). Vale la pena señalar que la fecha puede ser menor que 0 o mayor que el número máximo de días de este mes durante el proceso de extensión, lo que significa que la fecha abarca un mes.
    Mes: también incluye el año, usado para representar el eje año-mes
    Día: usado para representar el eje de fecha
    Primer día: usado para calcular el estilo izquierdo de la barra de tareas y la línea de hoy (gantt-today-line)

  2. Eje de tiempo extendido
    Cuando se monta, se preextiende con el día de hoy como centro y se extiende 10 semanas hacia adelante y hacia atrás. Al mismo tiempo, se coloca un visor cruzado a ambos lados del eje año-mes. Cuando el usuario se desplaza hacia él , automáticamente continuará extendiéndose por 4 semanas. $v.Gantt.inited se utiliza para evitar activar el desplazamiento antes de la inicialización.

  3. Cuando el mouse se mueve
    hacia la tarea en la columna fija izquierda (onMouseOver), la barra de tareas correspondiente se desplazará al rango visible (scrollIntoViewIfNeeded).
    Cuando se mueve el mouse a la barra de tareas, muestra la información de resumen de la tarea y arrastra el controlador hacia adelante y hacia atrás.

  4. Arrastrar
    el cuerpo de la barra de tareas y arrastrar el controlador hacia adelante y hacia atrás puede cambiar la hora de inicio y (o) la fecha límite de la tarea y, al mismo tiempo, mostrar dinámicamente el efecto visual de la capa de arrastre (gantt-drag-mask).
    Cuando se presiona el mouse, se comienza a monitorear el evento de movimiento del mouse y cuando se suelta el mouse, la hora de inicio y/o la fecha límite se modifican de acuerdo con la distancia/desplazamiento del movimiento.
    Si no hay desplazamiento al arrastrar el cuerpo de la barra de tareas, se debe considerar como una acción de hacer clic y una ventana emergente mostrará los detalles de la tarea.

Tarea

  1. Crear una nueva tarea
    A. Hay un botón "Nuevo" en la esquina superior derecha de los componentes de tipo Kanban, tabla y lista para crear una nueva tarea sin configuración preestablecida ;
    B. Cada grupo tiene un botón más para agregar una nueva tarea el mismo grupo : en $f.创建任务el evento de montaje, se $v.modal.分组asignará al campo utilizado para la agrupación. Si el campo es el estado, las posibles opciones de estado se filtrarán aún más. Lo mismo ocurre cuando hay un tipo de tarea principal; C En la ventana emergente de tareas, si está permitido Si
    hay un tipo de subtarea, puede crear una nueva subtarea , y la tarea principal está preestablecida en este momento.

  2. Proporción de progreso en este grupo de tareas
    Hay una barra de progreso encima de cada grupo de tareas, que indica respectivamente el número y la proporción de tareas completadas, en progreso y no iniciadas en este grupo.

  3. La parte superior de la ventana emergente de tareas
    consta del nombre del proyecto, el tipo de tarea, la matriz de organización de la tarea principal, el título de la tarea actual y sus respectivos íconos y colores.
    Se puede hacer clic en cada tarea principal para mostrar sus detalles.

  4. compartir para chatear

  5. Fijada a la barra de tareas
    $v.任务栏está la lista de tareas fijas del proyecto actual, que se puede expandir y contraer, eliminar y hacer clic para que aparezcan detalles.

  6. Tareas de archivo

  7. Al eliminar una tarea,
    también se elimina la actividad de la tarea y los recursos asociados a ella.
    Busque todos los recursos asociados, clone el archivo devuelto y elimínelos uno por uno:
    $r.arr.clone().forEach('$resource.delete($x._id)')

  8. Título
    Haga clic para cambiar al modo de edición y enfocar, pierda el foco o presione Intro para guardar los cambios y volver al modo de presentación.

  9. Estado actual
    Nombre para mostrar, ícono y color
    Haga clic para que aparezca una opción de estado transferible bajo el estado actual

  10. Responsable, hora de inicio, fecha límite

  11. La información básica
    muestra los atributos personalizados del tipo de tarea actual y sus valores en esta tarea.
    Se muestran según sus respectivos tipos de atributos, normalmente en 3 columnas, pero el texto enriquecido y el texto de varias líneas ocupan una sola fila.


  12. Se puede hacer clic en la barra de pestañas de subtareas y su lista para mostrar, disociar, eliminar tareas, cambiar la persona a cargo y la fecha de vencimiento.

  13. Tareas relacionadas
    Subtareas relacionadas e indirectas

  14. Adjuntos
    La capa superior de la ventana emergente de tareas es un componente de montaje, que buscará recursos asociados. Aunque la lista de archivos adjuntos no se muestra al principio, también se muestra el número de archivos adjuntos.
    Agregar, cambiar nombre, eliminar
    El ícono del archivo adjunto puede ser la miniatura de la vista previa del archivo local antes de cargarlo, o la miniatura después de cargar el recurso, o el ícono de archivo preestablecido según su tipo de archivo.

  15. Comentario

  16. Las actividades
    solo se muestran cuando el usuario hace clic, por lo que la carga se puede retrasar y almacenar por separado en el xtk asociado, y no es necesario almacenarlas en tareas como comentarios.

 

selector de fechas

El selector de fecha es una función de uso común y es un poco complicada. Podría haberse usado directamente eligiendo una biblioteca js de código abierto. Al principio, se escribió directamente para verificar la capacidad de la expresión.

  1. Inicializar antes de que aparezca el selector de fecha $v.pop:
    mantener: para evitar que se haga clic en el cuadro emergente, consulte un evento de clic global agregado en $c.exp.onLogin fecha
    : se establece la fecha/hora actual, si no , será
    la hora de hoy: ¿Necesita configurar la hora? Si la fecha anterior contiene minutos/segundos, será verdadera de forma predeterminada.
    F5: El propósito es activar el evento de montaje en el selector de fecha nuevamente para reinicializar $v.日期
    cb: Seleccione /borrar la fecha es la función de devolución de llamada llamada. Guardará/borrará la fecha correspondiente después de la verificación de "la fecha límite no puede ser anterior a la hora de inicio".

  2. El nivel superior del selector de fecha es un componente de montaje para la construcción $v.日期:
    Función F5: construye la matriz de fechas $v.date.arr de ese mes de acuerdo con el año y el mes en $v.date, y cámbiala cada vez que se monte o cambio de año y mes para llamar.
    El número de fechas es mayor que el número de días de un mes, y se utiliza una matriz bidimensional de 6 semanas, es decir, 6 * 7, por lo que algunas fechas al frente son del mes anterior y otras del mes anterior. atrás son del próximo mes. Tomamos como punto de partida el primer día de la semana de este mes, el menor que el mes anterior, mes - 1, fecha + el número de días del mes anterior; el mayor que el número de días del mes anterior. este mes es el mes siguiente, mes + 1, fecha: número de días de este mes.
    Además, el mes 13 es enero del año siguiente y el mes 0 es diciembre del año pasado.

  3. Fecha de retorno
    Si necesita configurar la hora, debe hacer clic en el botón "Aceptar" para devolver la fecha construida por el año, mes, día, hora y hacer clic en la fecha para actualizar el mes y el día en $v.date.
    Si no necesita configurar la hora y hacer clic en la fecha, devolverá inmediatamente la fecha construida por el año, mes y día seleccionados.

  4. Borrar
    Si necesita configurar la hora, el botón "Borrar" simplemente cierra el formulario de configuración de hora.
    Si no necesita configurar la hora, volverá vacío inmediatamente para que la persona que llama pueda borrar esta fecha.

  5. Entrada rápida de tiempo
    Además de ingresar horas y minutos directamente en el cuadro de entrada, puede hacer clic en las flechas arriba o debajo del cuadro de entrada para sumar o restar, o incluso presionar directamente las teclas de flecha hacia arriba y hacia abajo en el teclado para agregar rápidamente o restar. Para mejorar la eficiencia, los minutos se suman y restan en unidades de 5.
    Tenga en cuenta el manejo especial cuando las horas y los minutos están fuera de rango durante la suma y la resta.

  6. Sumar y restar año/mes
    Hay flechas dobles en la parte superior para sumar y restar años, e íconos de una sola flecha para sumar y restar meses. Tenga en cuenta que el mes puede pasar al año pasado o al año siguiente al realizar la verificación.

  7. Seleccione rápidamente un año/mes con un lapso grande.
    Cuando necesita seleccionar un año lejano, es un poco lento usar las flechas de arriba para sumar y restar paso a paso. Por ejemplo, para seleccionar la fecha de nacimiento de un anciano. , haga clic en el año y el mes en la parte superior central para seleccionar directamente el mes. Luego haga clic en el año en la parte superior central para ubicar rápidamente el año en unidades de 20 años.

 

aprobación

Diseñe el formulario de aprobación, establezca el proceso por condición, calcule las estadísticas detalladas y los aprobadores en tiempo real, muestre el proceso de aprobación, apruebe/rechace/transfiera/revoque la aprobación, comente y verifique registros, notifique al aprobador por mensaje instantáneo y envíelo. para charlar.

diseño de formulario

  1. Manejo de grupo

  2. gestión de plantillas

  3. Diseño de formulario de aprobación
    El diseñador de formularios es un editor visual de arrastrar y soltar. Es una herramienta simple de desarrollo de aplicaciones sin código, que incluye tres partes: controles opcionales, cuerpo del formulario y configuración de propiedades de control y formulario.
    Los atributos incluyen: tipo de atributo, nombre de atributo, texto del mensaje, si es obligatorio, etc.
    Los botones de opción, las casillas de verificación y las listas desplegables tienen listas de opciones.
    Un detalle es un contenedor de control especial que puede contener otros controles. Los controles de tipo numérico en los detalles también pueden tener dos atributos: "si participar en estadísticas detalladas" y "si ser un elemento de factor de proceso condicional".

dificultad principal:

  1. Arrastrar y soltar utiliza Sortable.js .
    Los puntos principales de arrastrar entre listas tienen el mismo nombre de grupo (nombre de grupo), y la opción de extracción de grupo (extracción de grupo) de la lista de control opcional usa clonar, lo que significa que cuando se extrae de la lista, se clona una copia en en cambio, el cuerpo del formulario no se arrastra y el grupo no puede agregar nuevos controles (ponga: falso). grupo: {nombre: "grupo", extraer: "clon", poner: falso}. Configuración
    recomendada de lista anidada : {fallbackOnBody: true, swapThreshold: 0,65}. La función onSort debe dividirse en ordenar entre controles en el cuerpo principal del formulario o extraer un control desde un control opcional. Al extraer, hay un pullMode en el parámetro que es clonar (correspondiente a la clonación de la opción de extracción anterior) .

  2. Al arrastrar un control de una posición a una nueva posición, primero elimínelo de la posición original (oldIndex) y luego insértelo en la nueva posición (newIndex)
    $fx property.splice($lenewIndex, 0, $fx property.splice ($leoldIndex, 1)[0])

  3. Configure la ruta de la propiedad del control al hacer clic para seleccionar un control de formulario: $v.Approval form.Property = "Property." + $index; y la ruta de la propiedad del control en los detalles es: $v.Approval form.Property = "Property. " + $parent.$index + ".property." + $index, están anidados en el control de detalles, para no activar el evento externo, use $event.stopPropagation() para evitar que el evento se propague y agregue onChange al editar las propiedades de los
    eventos de control, incluso los eventos vacíos, actualizarán/(presentarán) los controles correspondientes del cuerpo del formulario en tiempo real a medida que se selecciona la entrada o verificación, para tener una mejor experiencia de edición.

  4. Hay muchos tipos de controles y cada tipo tiene diferentes formas y propiedades, por lo que es necesario utilizar condiciones de representación para controlar qué tipo de control se muestra según los diferentes tipos. Puede enriquecer aún más la lista de control según las necesidades reales.

  5. Volver a representar
    El cuerpo del formulario está envuelto con un componente montado que hace que el formulario se vuelva a representar cuando se agrega u ordena un nuevo control con $v.F5属性un nuevo valor.
    Lo mismo ocurre con la lista de atributos del detalle, pero con otra variable $v.F5明细para volver a representar solo los atributos del detalle.

Definir el proceso de aprobación

Los diferentes tipos de aprobaciones, procesos de aprobación y aprobadores para cada proceso son diferentes

  1. Proceso libre
    El remitente puede elegir libremente en cuántos pasos consta la aprobación y quién necesita aprobar cada etapa. Es más flexible y adecuado para aprobaciones con baja complejidad y procesos de aprobación inciertos, pero no favorece la estandarización de la aprobación. proceso.

  2. Proceso fijo
    No importa cuáles sean las condiciones de la solicitud, el proceso de aprobación es fijo, lo cual es adecuado para la aprobación con un proceso fijo pero de baja complejidad.

  3. El proceso de aprobación más utilizado es el proceso de establecer condiciones
    por condición. Cuando las condiciones de la solicitud son diferentes, se requieren diferentes procesos de aprobación y aprobadores. Por ejemplo, cuando el monto del reembolso es inferior a 5000, solo se requiere el supervisor de primer nivel. para aprobación Cuando el monto del reembolso está entre 5,000 y 10,000, se requiere la aprobación del supervisor de Nivel 2, y se requiere la aprobación del jefe cuando el número es mayor a 10,000.

  4. Notificadores de aprobación
    La aprobación no requiere que algunos miembros participen en el proceso de aprobación, pero es necesario informar a TA sobre la aprobación.

dificultad principal:

  1. La columna de departamento es la estructura organizativa de la empresa y se pueden agregar uno o más aprobadores jerárquicamente por departamento. "Jefe de Departamento" es una variable indeterminada que será reemplazada por una persona específica cuando se inicie una aprobación.

  2. En el proceso de establecimiento de condiciones, el flujo de aprobación bajo ciertas condiciones se puede definir mediante expresiones, por ejemplo, cuanto mayor sea el número de reclutas o el monto del reembolso, mayor será el nivel de personas que deben participar en la aprobación. Presta atención a la continuidad de las condiciones y no dejes espacios.

 

iniciar la aprobación

  1. Complete el formulario de solicitud

  2. Cálculo en tiempo real de estadísticas detalladas.

  3. Calcule aprobadores en tiempo real
    Obtenga jefes de departamento

  4. Se requiere enviar para aprobación
    el formulario de validación.
    Generar número de aprobación

dificultad principal:

  1. Las estadísticas detalladas se dividen en total y total:
    el total es la suma de un determinado campo a contar y el total es la suma de la suma de cada campo a contar.
    Para acumulación: reducir("$acc + $x", 0)

  2. En el proceso condicional, busque el proceso que cumpla con las condiciones de la lista de procesos de aprobación de acuerdo con la expresión condicional preeditada ($x.condition). Un punto importante es que la expresión de condición de ejecución es pasar el contenido del formulario de solicitud ($f. aplicación) como entorno de ejecución, otro punto importante es encontrar la lista de aprobadores del proceso, es necesario clonar los aprobadores pendientes. asignado al formulario de solicitud, porque el próximo cambio del proceso de aprobación cambiará a la persona que será aprobada (como reemplazar al jefe del departamento y transferirlo al aprobador), y no queremos afectar al aprobador en el proceso original. .
    $f.Application.Aprobadores pendientes = Proceso de aprobación.find('exc($x.Conditions, $f.Application)').Approvers.clone()

  3. La función "Buscar supervisor de departamento" se define en la página onReady, que se ejecutará cuando se cargue el formulario de solicitud y también se ejecutará cuando se ingresen números en el proceso condicional.
    La dificultad es llamar recursivamente a "$exp.Department Supervisor", y el departamento se busca recursivamente de arriba a abajo hasta que stopIf() lo detiene cuando llega al departamento del solicitante. Cada recursividad pasa el departamento actual al entorno de ejecución de expresiones.

  4. El número de aprobación es la fecha de solicitud + número de serie de 4 dígitos. El número de serie es un número único que comienza en 1 cada día y se obtiene aumentando ($inc) 1 de la base de datos para cada aplicación.

  5. Al enviar para aprobación, elimine al primer aprobador de la lista de aprobadores pendientes como aprobador actual y envíe un mensaje instantáneo a TA.

Pantalla de aprobación

  1. estado actual

  2. solicitante

  3. Detalles de aprobación
    Visualización de varios tipos de datos
    Detalles y total, total

  4. Proceso de aprobación

  5. informar a la gente

  6. Comentario

  7. verificar registros

dificultad principal:

  1. El reembolso categorizado puede mostrar los detalles de aprobación de una manera más completa. Su capa exterior es un componente de datos de un elemento de tabla, que consta de 4 cuerpos, pero controla el segundo componente de datos utilizado para mostrar los detalles. El primer cuerpo está vacío El tercero uno se usa para mostrar la lista de atributos y el cuarto se coloca en la parte inferior para mostrar el total.
    Vale la pena considerar que el segundo componente de datos utilizado para mostrar los detalles no está configurado con elementos HTML, es decir, no se representará como un elemento HTML en sí, sino que controlará la representación de su primer elemento secundario, es decir, para muestra la lista de detalles en un bucle (p. Los dos subelementos se usan para mostrar el total), y cada detalle tiene múltiples atributos, y el componente de datos se usa nuevamente, y hay atributos de matriz en el detalle para usar el componente de datos (como cargar múltiples archivos adjuntos).
    Este es un ejemplo de cómo anidar componentes de datos para mostrar datos complejos al extremo.

  2. El componente de datos en el proceso de aprobación controla el segundo li, que se obtiene fusionando el flujo de aprobación, el aprobador actual y la persona a aprobar. El primer li está fijo para el solicitante y no está controlado por el componente de datos.
    El [flujo de aprobación] aquí se refiere a las personas que han participado en la acción de aprobación, que puede incluir a la persona de revocación (el propio solicitante) y al cesionario. Cada acción envía información relevante al flujo de aprobación: $push: { "x. flujo de aprobación": { quién: $c.me._id, hora: fecha(), acción, opinión} }

  3. La entrada del comentario no se utiliza para la entrada, pero cuando se hace clic en él (let $v.modal.comment = true cuando está en Focus), aparece un formulario de comentario con un área de texto y el formulario de comentario se puede retraer haciendo clic en otro lugar porque es en un lugar de nivel superior (modal -detail-main) Deje $v.modal.comments vacío.

  4. Hay un componente de montaje en la ventana modal de aprobación. Cada vez que un no solicitante abre la ventana modal de aprobación, se agregará un registro de revisión a [Revisión de aprobación].

  5. Para reducir la cantidad de información de aprobación, tanto los comentarios de aprobación como las referencias de aprobación se almacenan en la tabla xtk asociada, con aprobación._id como clave. Los comentarios y la revisión de aprobación no se leen en varios escenarios de listas de aprobación. Cuando se abre la ventana modal de aprobación, los comentarios se leen de forma predeterminada en el componente de montaje anterior, pero la revisión de aprobación aún no se lee.

Proceso de aprobación

  1. Aceptar aprobar

  2. Negarse a aprobar

  3. Reenviar para aprobación

  4. aprobación revocada

  5. Aprobación de comentarios

  6. Ver aprobación

aviso de aprobación

  1. Yo solicité

  2. yo aprobé

  3. Infórmame

  4. notificación de mensaje instantáneo

  5. enviar al chat

dificultad principal:

  1. Obtenga los últimos datos/representación.
    Movimientos pendientes entre diferentes personas y la información cambia rápidamente. Además de querer notificar a "yo" de las solicitudes que esperan que "yo" sea aprobada mediante mensajes instantáneos, también espero recibir solicitudes [pendientes de aprobación] relacionadas con "yo" cuando esté en la página de aprobación.
    Número de aprobaciones pendientes: hay un componente de montaje en el nodo raíz, que se utiliza como $v.F5expresión clave. Siempre que $v.F5 cambia (enviar, revocar, aceptar, rechazar y transferir para aprobación), la aprobación pendiente se relaciona con " I" se extraerá nuevamente
    . Lista de aprobación de cantidad: la tabla de la lista de aprobación está empaquetada con un componente de montaje $v.tab + $v.F6para la expresión clave. Al cambiar en la barra de navegación izquierda (aplicado, aprobado, notificado) o en la barra de pestañas en la esquina superior derecha (pendiente de aprobación, aprobado), se extraerá nuevamente la última lista de aprobación.

Supongo que te gusta

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