高仿項目協(xié)作工具【W(wǎng)orktile】,從零帶你一步步實現(xiàn)組織架構(gòu)、網(wǎng)盤、消息、項
時間:2023-04-23 23:00:01 | 來源:網(wǎng)站運營
時間:2023-04-23 23:00:01 來源:網(wǎng)站運營
高仿項目協(xié)作工具【W(wǎng)orktile】,從零帶你一步步實現(xiàn)組織架構(gòu)、網(wǎng)盤、消息、項目、審批等功能:
聲明
本課程于2020年8月6日仿自杭州易成星光科技開發(fā)的Worktile,相關(guān)素材版權(quán)歸屬于原網(wǎng)站,本課程作為仿站習(xí)作,僅做教學(xué)用,不得用作商業(yè)用途,商業(yè)合作請訪問源站。
課程開發(fā)前試用了市面上幾乎所有企業(yè)協(xié)作辦公平臺,選中Worktile是覺得它是當(dāng)時最優(yōu)秀的,也喜歡它的UI風(fēng)格,推薦有需要的朋友選購。
本課程目的是演示一個復(fù)雜的協(xié)同類OA的SaaS應(yīng)用軟件,基于眾觸這樣一個專業(yè)的低代碼應(yīng)用平臺,如何開發(fā)一個復(fù)雜的功能完整的SaaS應(yīng)用。Worktile是專為企業(yè)辦公場景設(shè)計的項目協(xié)作管理系統(tǒng),業(yè)務(wù)概念多,關(guān)系錯綜復(fù)雜;雖然大家很可能不需要做到這種復(fù)雜度的應(yīng)用,但學(xué)習(xí)里面用到的思想和用法是非常有價值的。
源站隨著時間的推移可能會不斷迭代演變,而課程作品并不會一起跟著變化,所以作品跟你現(xiàn)在看到的源站不同是正常的。
本課程有非常詳盡的視頻講解每個功能模塊,都是是從空白頁面開始一步步實現(xiàn)的:總覽、組織架構(gòu)、網(wǎng)盤、即時消息、項目管理、審批。
賬號
為了演示公司的組織架構(gòu),虛擬了以下人員:
- CEO:戴國強 13845678901 管理員
- 總經(jīng)辦經(jīng)理:趙敏 13845678911 管理員
- 人事總監(jiān):陳穎 13845678902 管理員
- 財務(wù)總監(jiān):付媛 13845678906 管理員
- 項目一組組長:曾輝 13845678312
- 項目一組開發(fā):黃鴻亮 13845678909
- 項目一組開發(fā):黃姚謙 13845678223
- 技術(shù)總監(jiān):謝林華 13845678907
- 項目組經(jīng)理:孫榮浩 13845678905
- 平臺組組長:葛傳富 13845678910
- 測試組組長:王伯祥 13845678908
- 尚未分配部門:黃飛凱 13845678102
這些人物和手機都是瞎編的,頭像來自我來筆記。
本課程應(yīng)用作為供學(xué)習(xí)者克隆的模板,數(shù)據(jù)不能太亂,沒有提供演示賬號也沒開放注冊,所以即使克隆了本應(yīng)用也還不能直接登錄。克隆以后到登錄頁面(/z/login),它使用的是賬號登錄管理插件,右鍵選中插件,在右邊面板激活“注冊”后保存再刷新頁面即可注冊了,注冊后自己有賬號可以登錄了但還是沒有密碼以上面這些人員的賬號登錄,可以先在”后端安全“中注釋掉$user.toggleRole里的限制,再在”成員管理“中點擊某個成員的”設(shè)置“,在”修改成員賬號“標(biāo)簽頁中修改此成員的密碼后就有密碼以此人身份登錄了。詳細指導(dǎo)請移步教學(xué)視頻。
準(zhǔn)備深入研究的同學(xué)請注冊登錄眾觸官網(wǎng)后,點擊克隆按鈕,把整個應(yīng)用復(fù)制一份隨意調(diào)試更改吧。
全局$V.s
全局可持久化狀態(tài),
s
是
status首字母。應(yīng)用開始時從localStorage讀取,應(yīng)用結(jié)束時(onbeforeunload)存回localStorage,這樣可以立即恢復(fù)回上次離開時的狀態(tài)。
- $V.s.左導(dǎo)航:最新點開的左側(cè)導(dǎo)航欄標(biāo)簽
- $V.s.消息:最新點開的私聊聯(lián)系人或群組的_id
- $V.s.未讀:各私聊或群組的最新未讀消息的個數(shù)
- $V.s.已讀:各私聊或群組的最新已讀消息的時間
- $V.s.折疊:各種被折疊的菜單
- $V.s.項目:最新點開的項目_id及其組件下標(biāo)和視圖下標(biāo)
- $V.s.任務(wù)欄:以項目為單位固定到任務(wù)欄的任務(wù)列表
全局事件
- click:當(dāng)有彈窗
$v.pop
時關(guān)閉彈窗,但keep
為真時點擊彈窗內(nèi)部或者zpage以外(confirm或alert)不關(guān)閉。 - keydown:當(dāng)有彈窗或模態(tài)窗時按下退出鍵
Escape
可關(guān)閉彈窗或模態(tài)窗。
組織架構(gòu)
公司的組織架構(gòu)是其他功能的基礎(chǔ)。 可以以樹狀結(jié)構(gòu)管理各部門及其成員,也可以從通訊錄里查看同事正在經(jīng)手的任務(wù)狀態(tài)。
- 職位列表
可添加、修改、上移、下移、刪除。 - 用戶登錄時($c.exp.onLogin)拉取公司全員工信息和公司組織架構(gòu),即$c.exp.部門。
- 對員工和組織架構(gòu)整合到全局變量中,方便應(yīng)用的很多地方使用。
$V.部門
是組織架構(gòu)的克隆,除了增加了個”未分配部門“
$V.部門成員
是把組織架構(gòu)拍平后把各部門成員加進去
$V.部門總成員
是在$V.部門成員基礎(chǔ)上再把所有子孫部門成員都加進去 - 成員管理
基本信息設(shè)置,修改成員賬號,角色設(shè)置 - 調(diào)整部門
可以選中成員(單選或全選)后可批量調(diào)整他們的部門 - 成員搜索(過濾)
重點、難點:
- 移動就是交換位置,常用原生函數(shù)
arr.splice(新下標(biāo), 0, arr.splice(舊下標(biāo), 1)[0])
,先從舊下標(biāo)刪除一個元素,刪除得到的是數(shù)組,所以后面加個[0]
取得元素本身,再把被刪除元素插入到新下標(biāo)中。
也就容易理解$l.arr.splice($index - 1, 0, $l.arr.splice($index, 1)[0])是上移,$l.arr.splice($index + 1, 0, $l.arr.splice($index, 1)[0])是下移了。
等學(xué)習(xí)了后面課程掌握了如何使用Sortable.js,回過頭來把這里的上移和下移按鈕改成拖動排序?qū)⑹共僮鞲唵魏椭庇^。 - 在數(shù)據(jù)庫中刪除數(shù)組的某個元素常用$pull,即從數(shù)組中拉走某個元素:
$xtk.modify("公司", "職位", {$pull: {"x.arr": $x}}) - 數(shù)據(jù)結(jié)構(gòu)
部門是以樹形結(jié)構(gòu)存儲的,部門有名稱、主管,zchildren數(shù)組放子部門。
數(shù)據(jù)組件的數(shù)據(jù)源包含zchildren時會遞歸渲染成樹形結(jié)構(gòu)。 - 子部門縮進
為了體現(xiàn)部門層次結(jié)構(gòu)在動態(tài)樣式里根據(jù)層次深度($indexes)計算左縮進(paddingLeft) - 部門調(diào)整或成員更新后通過
$v.F5
讓掛載組件獲取最新數(shù)據(jù)重新渲染 - 刪除成員的部門用的是
$unset
。留意“未分配部門”這個特殊部門。 - 刪除子部門用的是
$pull
,從數(shù)組中“拉走”。
組織架構(gòu)通訊錄
- 同事列表
- 同事詳情
- 同事任務(wù)列表及狀態(tài)數(shù)
- 發(fā)消息
- 常用聯(lián)系人
- 收藏
- 按部門查找同事
通訊錄網(wǎng)盤
類似百度網(wǎng)盤、阿里網(wǎng)盤,以虛擬文件夾的形式分層分類來管理云端存儲的文件,可批量上傳整個文件夾。
網(wǎng)盤展示
- 讀取文件夾及文件信息。
- 文件夾樹
- 文件路徑
- 搜索
- 排序
重點、難點:
- 樹形數(shù)據(jù)結(jié)構(gòu)
文件夾只是存儲于product表中的簡單記錄,包含名稱
、顏色
、父
(即父文件夾)信息,而非直接的樹形結(jié)構(gòu),但我們要根據(jù)“父”構(gòu)造出類似于上節(jié)課的樹形組織架構(gòu)來,作為數(shù)據(jù)組件的數(shù)據(jù)源。
文件夾可以有非常深的層次結(jié)構(gòu),不像組織架構(gòu)那么單一穩(wěn)固,不能一次性把所有的文件夾都讀取出來。我們在onReady先讀取頂層文件和頂層文件夾(其父文件夾是"無"),并依次讀取各自直接子文件夾(根據(jù)有無子文件夾決定是否顯示“展開”小三角圖標(biāo))。
讀取文件或文件夾的一個要點是把父文件夾_id傳入執(zhí)行環(huán)境:{ 父: _id }
。
文件是上傳到對象存儲服務(wù)器的普通資源,并不無文件結(jié)構(gòu)樹信息,所以我們每次上傳完文件后要給資源表添加當(dāng)前文件夾作為其父文件夾:
$resource.modify(_id, { "x.類型": "網(wǎng)盤", "x.父": 當(dāng)前文件夾_id }) - 幾個關(guān)鍵變量
$v.根
是文件夾結(jié)構(gòu)樹的根(名稱喚作“網(wǎng)盤”吧,文件路徑的第一個節(jié)點),是最大的一棵樹。它的“zchildren”包含上面讀取頂層文件夾,”文件“包含頂層文件。特別地,它的_id為”無“。
$v.樹
是以父文件夾_id作為key的各級文件夾起始的大大小小的樹。所以$v.樹.無
也即$v.根
。
$v.路徑
是樹根到當(dāng)前文件夾各級文件夾樹的數(shù)組
$v.文件夾
是當(dāng)前文件夾_id。用戶點擊時(無論是左側(cè)文件結(jié)構(gòu)樹還是主體文件夾列表)會以它作為父文件夾讀取它里面的文件夾和文件信息。另外,點擊”展開“小三角圖標(biāo)時只需讀寫孫輩文件夾信息,一方面樹里不展示文件信息,另一方面子文件夾已經(jīng)讀取,孫輩文件夾信息是為了決定是否展示子文件夾的小三角。 - 文件夾(自定義組件)
動態(tài)顏色 - 重新渲染
當(dāng)$v.F5
變化時有三個掛載組件會重新組裝數(shù)據(jù)并再次渲染。
A. 左側(cè)文件結(jié)構(gòu)樹里的掛載組件就是用來組裝$v.樹
的,從根節(jié)點開始向下遞歸遍歷所有已經(jīng)搜索出來的文件夾:一方面把文件夾直接掛載$v.樹
上,另一方面把它推入父文件夾的zchildren數(shù)組中。
B. 頂部的文件路徑里的掛載組件就是用來組裝$v.路徑
的,從當(dāng)前文件夾開始向上遞歸直至根節(jié)點,把每層文件夾推入路徑數(shù)組頭部(unshift)。
C. 主體文件列表里的掛載組件就是用來組裝當(dāng)前文件夾里的文件列表的。 - 搜索、排序
我們總是先展示文件夾列表,然后才是文件列表,是分開搜索分開排序的,再把兩種拼接起來:
($v.搜索.文件夾 || $v.樹[$v.文件夾].zchildren || []).sort($v.sort.key, $v.sort.incr).concat(($v.搜索.文件 || $v.樹[$v.文件夾].文件 || []).sort($v.sort.key, $v.sort.incr))
網(wǎng)盤展示網(wǎng)盤上傳
- 新建文件夾
- 上傳文件(可多選)
- 上傳文件夾(保持文件結(jié)構(gòu))
- 上傳列表,上傳進度和狀態(tài)
重點、難點:
- 上傳文件
按鈕觸發(fā)的是隱藏在里面的input元素:$el.firstElementChild.click()。
把上傳成功后才要用到的$l.U
在剛開始選擇文件(onChange時)就設(shè)置了是為了防止上傳過程中受到用戶點擊了其他文件夾導(dǎo)致$v.文件夾發(fā)生變化的影響,而刷新用戶當(dāng)前文件列表($v.exp.文件.exc({父: $v.文件夾}))則沒有這個憂慮。 - 上傳文件夾
為了避免受$v.文件夾變化的影響先賦值給臨時變量:$l.文件夾 = $v.文件夾
把整個文件夾遞歸上傳的關(guān)鍵是每個待上傳文件有個相對路徑webkitRelativePath
,循環(huán)每個路徑節(jié)點,創(chuàng)建未創(chuàng)建的文件夾節(jié)點,直到最里層的文件夾賦給$l.當(dāng)前文件夾,上傳完成后把它作為父文件夾。 - 上傳列表
文件上傳前把信息推入$v.上傳
數(shù)組,標(biāo)識為”正在上傳“,如果是圖片或視頻給它創(chuàng)建一個縮略圖:URL.createObjectURL(file),上傳過程中及時改變它的進度樣式,上傳完成后刪除”正在上傳“標(biāo)識。
網(wǎng)盤上傳網(wǎng)盤管理
- 重命名
- 移動
- 刪除
- 修改顏色
- 發(fā)送到聊天
- 回收站
重點、難點:
- 我們刪除文件夾或文件是讓它脫離原來的位置,但隨后有還原操作,所以還要保留原來位置的信息。
我們通過把父文件夾改成原父文件夾的形式來實現(xiàn)
$rename: {"x.父": "x.原父"}
這樣沒有父文件夾的資源就是被邏輯刪除的。
"x.原父": {$exists: true}
網(wǎng)盤管理即時消息
類似企業(yè)微信/飛書/釘釘?shù)募磿r消息,支持私聊、群聊、歷史消息、置頂,可以發(fā)送表情、圖片、網(wǎng)盤文件、項目和審批到會話,還可以收藏文件、固定消息。 是學(xué)習(xí)連接的好課程。
消息初始化
- 獲取私有群(群成員包含我)和公開群的列表信息,并把它們的_id組成列表
$V.消息.群ID
。 - 打開連接
$socket.open()
:
群組_id列表作為channels
,這樣可收到相關(guān)群組的消息;
saveToDB
設(shè)為true,把信息都保存到數(shù)據(jù)庫,這樣通過$socket.hist()可以查看歷史消息,即時不在線是別人發(fā)送的未讀消息也能收到;
allowMultiLogin
設(shè)為true,這樣打開的多個設(shè)備/瀏覽器都可以同時收到消息。 - onConnect 已連上
通過$socket.hist()
獲取所有私聊的歷史消息。
通過$socket.hist($V.消息.群ID)
獲取所有私有群和公開群的歷史消息。
給各消息添加發(fā)送消息日期,為了方便在消息列表中顯示分割用的日期和星期。
獲取置頂會話的_id列表。
根據(jù)私聊對象_id或群聊_id:
把各消息列表放入$V.消息.chat
中;
把各更多消息的列表放入$V.消息.more
中,每當(dāng)聊天窗口滾到頂部時就從中拿出一個_id去獲取更早的歷史消息;
把各消息_id放入$V.消息.histID
中,刪除歷史消息時用;
把各消息的發(fā)送時間大于其已讀時間$V.s.已讀
的消息條數(shù)放入其$V.s.未讀
中; - onData 收到新消息
根據(jù)聊天對象(私聊對象_id或群聊_id)把消息放入對應(yīng)的消息列表$V.消息.chat
中。
如果此消息是當(dāng)前正在打開的聊天會話中,則渲染后把新消息滾入視圖,否則給其未讀消息數(shù)加 1。
如果此消息包含@我的信息,彈出一個通知窗口notification(如未被阻止)。
消息會話
- 創(chuàng)建群組/群組設(shè)置
- 加入群組
- 發(fā)起私聊
- 會話列表。無論置頂會話是否有消息都全部先展示出來,然后展示除去置頂?shù)挠邢⒌臅挘?br>$V.消息.置頂.concat($V.消息.chat.keys().filter('!$V.消息.置頂.includes($x)'))
- 會話過濾:
.filter('$f.search.kw ? ($c.user[$x].x.姓名 || $c.xdb[$x].x.name).includes($f.search.kw) : 1') - 未讀消息,包含歷史未讀和即時未讀新消息。
未讀總數(shù):$V.s.未讀.values().reduce('$acc + $x', 0) - 點擊會話時
把會話ID賦給$V.s.消息,渲染消息列表后滾到最新一條消息
如果未讀消息數(shù)小于6時把所有消息設(shè)為已讀(清空未讀并記錄最新消息的發(fā)送時間) - 刪除會話
A. 刪除關(guān)聯(lián)會話的文件列表
B. 刪除文件列表中的文件本身
C. 刪除關(guān)聯(lián)會話的固定消息列表
D. 刪除整個消息歷史。
即時消息
- 消息列表
$V.消息.chat[$V.s.消息] - 當(dāng)跟前一個消息不是同一天時展示日期分割。
date !== $array[$index - 1].date - 把自己發(fā)送的消息向右對齊,以區(qū)別于別人發(fā)送的消息
$c.me._id === from ? " message-item--me" : "" - 消息彈出框
固定消息,刪除自己發(fā)送的消息 - 用
HTML組件
渲染消息體 - 交叉觀察器
如果還有更多消息未讀取出來時($V.消息.more[$V.s.消息].length),當(dāng)滾到消息框頂部是會觸發(fā)交叉觀察器從$V.消息.more
里拿出一項交給$socket.more()來獲取更多消息,并放入消息列表頭部,渲染后滾到第一個消息但交叉觀察器卻在窗口外的位置,這樣避免連續(xù)觸發(fā),而是等用戶再往上滾的時候又會觸發(fā)一次。
一個注意點是引入臨時變量$v.l.more來阻止每次新渲染交叉觀察器時導(dǎo)致的首次執(zhí)行,因為此時并不是由有戶主動往上滾引起的。 - 未讀消息
展示未讀消息數(shù),點擊時往上滾到上次的已讀消息或者消息框頂部以觸發(fā)獲取更多消息的交叉觀察器,然后觸發(fā)“全部標(biāo)記為已讀” - 發(fā)送文本消息
把[表情文本]
替換成對應(yīng)的img:.replace($c.reg.表情, $c.fun.表情);
@某人:在群組會話中按下@時(即Shift + 2,2的keyCode是50)彈出排除自己的群組成員列表,但在已彈窗時按下非Shift鍵(keyCode為16)則關(guān)閉彈窗;
把@24位的某人_id
替換成對應(yīng)的姓名:.replace($c.reg.提及, $c.fun.提及);點擊時還有成員
彈窗是因為添加了onClick事件:window.提及click();
$socket.send($V.s.消息, "text", $f.消息.txt); - 發(fā)送圖片/文件消息
點擊文件圖標(biāo)是彈出上傳對話框,選擇一個或多個文件后上傳至服務(wù)器;
然后根據(jù)不同的文件類型轉(zhuǎn)化成對應(yīng)的HTML文本:i
為圖片,直接顯示圖片縮略圖;v
為視頻,顯示對應(yīng)的視頻截圖;f
為其它文件類型,會根據(jù)其文件后綴format來顯示不同的圖標(biāo));
消息發(fā)送后把文件信息添加到此會話的文件列表中。
值得注意的是消息發(fā)送完了并沒有立即操作數(shù)據(jù)庫而是先把內(nèi)容放在變量$v.待保存文件消息中,等待消息返回后在$c.exp.onData再執(zhí)行,因為考慮到以后刪除文件時也要把它從消息歷史中刪除,刪除時要用到消息發(fā)送時間d:$socket.pull(_id, from, d),而這個時間是在服務(wù)器端生成的,只有收到消息后才知道。 - 發(fā)送項目任務(wù)/審批/網(wǎng)盤消息
其它
- 文件列表
從網(wǎng)盤發(fā)送到聊天的文件不在此列表中
刪除一個文件要:
A. 刪除文件本身
B. 從關(guān)聯(lián)會話的文件列表中移除
C. 消息歷史中移除對應(yīng)消息。 - 固定消息列表
刪除時從關(guān)聯(lián)會話的固定消息列表中移除 - 點擊成員圖標(biāo)
群組時顯示群組成員,可進一步顯示成員資料或移除成員
私聊時顯示成員資料 - 點擊設(shè)置圖標(biāo)
置頂會話和取消置頂會話
群組設(shè)置
退出群組
刪除群組
即時消息項目管理
項目協(xié)作管理是Worktile的核心功能,用來計劃、組織和跟進一些具有明確目的且相互關(guān)聯(lián)的任務(wù)。 支持高度靈活的個性化配置以適應(yīng)各種場景的項目需求。
Worktile以項目化的方式計劃、管控和跟進多種工作領(lǐng)域和場景中的任務(wù)。
項目管理概念
項目
任務(wù)集合中心,用來計劃、組織和跟進一些具有明確目的且相互關(guān)聯(lián)的任務(wù)。項目可以是長期的也可以是短期的。
組件
任務(wù)的展示方式,將任務(wù)信息以不同的視覺形式呈現(xiàn)給用戶,有多種組件類型:看板、列表、表格、甘特圖、日歷、報表。
視圖
組件可以有多種視圖來對任務(wù)進行分組、篩選、排序。
任務(wù)類型
承載不同業(yè)務(wù)場景的模型,由高度結(jié)構(gòu)化的任務(wù)屬性構(gòu)成,可規(guī)定任務(wù)類型之間的關(guān)系和狀態(tài)流轉(zhuǎn)方式,從而形成信息存放和團隊協(xié)作的規(guī)范。
屬性:任務(wù)類型可以指定此類型的任務(wù)可包含哪些自定義信息,有多種屬性類型:文本、富文本、數(shù)字、日期、成員、多個成員、下拉單選、下拉多選、繼承。
任務(wù)
某種任務(wù)類型的實例。每個任務(wù)都有標(biāo)題、狀態(tài),還有負責(zé)人、參與人、起止時間、評論等,主體部分是任務(wù)類型里定義的各種屬性。
建議大家克隆本課程后嘗試擴展更多屬性類型,甚至擴展組件類型。
數(shù)據(jù)模型
項目:
{ 名稱: String, 任務(wù)類型: [ID], 描述: String, 顏色: String, 置頂: Boolean, 參與人: [ID], 組件: [{ 類型: String, 名稱: String, 任務(wù)類型: ID, PC顯示: [String], 視圖: [{ 名稱: String, 分組: String, 篩選: String, 排序: String }] }]}
任務(wù)類型:
{ 名稱: String, 項目: ID, 父任務(wù)類型: ID, 圖標(biāo): String, 描述: String, 狀態(tài): [{ 名稱: String, 顏色: String, 類型: Number, 默認(rèn): Boolean }], 狀態(tài)流: {}, 屬性: [{ 字段: String, 類型: String, 選項: [], 新建: Boolean, 必填: Boolean }]}
任務(wù):
{ 標(biāo)題: String, 項目: ID, 任務(wù)類型: ID, 父任務(wù): ID, 關(guān)聯(lián)任務(wù): [ID], 負責(zé)人: ID, 開始時間: Number, 截止時間: Number, 評論: [{ txt: String, auth: ID, d: Date }], 狀態(tài): { 名稱: String, 顏色: String, 類型: Number }, 屬性: { ... } // 例子如下}
其中任務(wù)屬性是存儲它所屬任務(wù)類型屬性指定的具體數(shù)據(jù),比如招聘任務(wù)的屬性:
{ 職位: String, 部門: String, 薪資范圍: String, 需求人數(shù): Number, 在職人數(shù): Number, JD描述: String, 參與人: [ID]}
安裝數(shù)據(jù)庫管理插件可以更直觀地理解數(shù)據(jù)模型。
初始化
加載項目
- 加載我參與的私有項目
- 加載公開項目
- 把以上兩種項目合并后抽取簡要信息組成
$v.項目.菜單
列表 - 從
$v.項目.菜單
過濾出$v.項目.置頂
列表 - 如果有
$V.s.項目._id
就繼續(xù)加載此項目信息 - 加載此項目的任務(wù)類型
- 加載此項目的所有任務(wù)
- 恢復(fù)此項目的
任務(wù)欄
數(shù)組
任務(wù)整理
把上面加載的原始數(shù)據(jù)整理到
$v.任務(wù)
。
- 如果任務(wù)類型里有指定父任務(wù)類型,就把它添加到其父任務(wù)類型的
$v.任務(wù).子任務(wù)類型
里 - 把各任務(wù)類型里自定義的屬性賦給對應(yīng)的
$v.任務(wù).屬性
列表中 - 為了方便讀取,繼續(xù)把上述屬性列表根據(jù)它里面的字段賦給對應(yīng)的
$v.任務(wù).字段
對象中 - 不更改原$c常量,克隆所有任務(wù)數(shù)據(jù)賦給
$v.任務(wù).arr
數(shù)組,并根據(jù)其_id組成$v.任務(wù).O
對象 - 如果一個任務(wù)有父任務(wù),那就把該任務(wù)根據(jù)任務(wù)類型添加到其父任務(wù)的子任務(wù)數(shù)組
x.子任務(wù)
里,此外遞歸往上尋找父任務(wù)鏈條組成任務(wù)鏈數(shù)組x.父任務(wù)arr
,作為導(dǎo)航欄展示在任務(wù)彈窗頂部 - 對于任務(wù)類型里定義的繼承類型的屬性,給其下所有任務(wù)里的此屬性添加繼承字段
分組整理
把上面加載的原始數(shù)據(jù)整理到
$v.分組
- 把當(dāng)前活躍的項目組件(即$V.s.項目.組件)賦給
$v.分組.組件
- 把當(dāng)前活躍組件的任務(wù)類型下的任務(wù),經(jīng)過當(dāng)前活躍視圖篩選要求過濾后,根據(jù)當(dāng)前活躍視圖規(guī)定的分組字段進行分組,把每個任務(wù)添加到
$v.分組.O[分組屬性]
數(shù)組中,不同的分組屬性組成$v.分組.arr
數(shù)組 - 根據(jù)任務(wù)狀態(tài)統(tǒng)計各分組下的任務(wù)數(shù),并合計分組總數(shù)
- 根據(jù)當(dāng)前活躍視圖的排序要求給各分組里的任務(wù)列表進行排序
- 把當(dāng)前任務(wù)類型的
$v.任務(wù).字段
賦給$v.分組.字段
- 對甘特圖組件做額外處理
任務(wù)活動
把任務(wù)活動封裝成函數(shù),以方便在多個地方任務(wù)發(fā)生變化時調(diào)用:把傳入的事件參數(shù)添加上變更人和變更時間后插入到任務(wù)對應(yīng)的xtk數(shù)組頭里。
任務(wù)彈窗
從其他頁面跳轉(zhuǎn)過來帶有“任務(wù)”參數(shù)時($query.任務(wù))彈出任務(wù)模態(tài)框。
onResize
當(dāng)窗體不足以容納所有組件時把后面的組件/視圖收到“更多”里面。
$v.項目.顯示
表示可以容納的組件數(shù)。
項目
- 項目列表、搜索
- 置頂、取消置頂
- 查看歸檔任務(wù)、激活歸檔任務(wù)
- 邏輯刪除項目
- 置頂列表
- 回收站
- 創(chuàng)建項目
- 抽屜
抽屜把手.drawer-handler點擊時會切換$V.s.折疊.左菜單的狀態(tài)從而切換.drawer-wrap的類名drawer-wrap-collapse。 - 組件列表
只展示特定個數(shù):組件.slice(0, $v.項目.顯示) - 更多組件
組件.slice($v.項目.顯示, 組件.length)
選中更多列表里的組件時顯示組件名稱:$V.s.項目.組件 >= $v.項目.顯示 ? 組件[$V.s.項目.組件].名稱 : "更多" - 視圖和更多視圖
項目管理任務(wù)類型
- 新建任務(wù)類型與修改任務(wù)類型
- 屬性配置
屬性是保存到數(shù)據(jù)庫中的關(guān)于任務(wù)的字段。
預(yù)定義屬性:當(dāng)前狀態(tài)、負責(zé)人、開始時間、截止時間 - 添加自定義屬性
“屬性類型”決定了屬性的展示方式,選定后不可更改。
如有“父任務(wù)類型”則添加“繼承”屬性,有專屬的“源屬性名稱”下拉框以選擇可繼承屬性,包括父任務(wù)類型的預(yù)定義屬性和自定義屬性,繼承可遞歸/追溯到頂級祖宗任務(wù)。 - 定義自定義屬性
選中一個自定義屬性后配置其屬性。
“屬性名稱”是展示和保存到數(shù)據(jù)庫的字段名,下拉框有選項列表,打開“展示在新建頁上”開關(guān)會在“新建任務(wù)”彈窗中展示,并出現(xiàn)“必填”選項,“必填”開關(guān)會在屬性名前添加紅色星號,并在入庫前檢查是否已填。 - 排序與刪除自定義屬性
- 狀態(tài)配置
狀態(tài)列表決定了一個任務(wù)的所有可能的狀態(tài)。
狀態(tài)有名稱、顏色、類型和備注。
狀態(tài)有3中類型:未開始、進行中、已完成,決定了任務(wù)分組中的進度條的3種顏色。
初始狀態(tài)是新建任務(wù)的默認(rèn)狀態(tài),所以只可能有一個初始狀態(tài)。 - 狀態(tài)流
狀態(tài)流是指特定狀態(tài)可變更成哪種幾種狀態(tài),未勾選或已清除狀態(tài)流則一個狀態(tài)可以變更成任何一種狀態(tài)。 - 添加、編輯、移除、保存狀態(tài)
任務(wù)類型組件
- 添加、編輯、刪除、排序組件
每種任務(wù)類型可以添加多種不同形式的組件
但報表組件與任務(wù)類型無關(guān),一個項目也只需要一個報表組件 - 組件視圖
每個組件可以有各種不同視圖,定義其分組、篩選、排序的方式。
編輯、添加、刪除 - PC顯示
定義在PC端顯示任務(wù)列表時應(yīng)該顯示哪些屬性
添加、刪除、排序要顯示的屬性 - 看板
看板的主要特點是可以拖拽分組或排序
拖拽一個任務(wù)到另一個分組時要修改其分組的字段到新組名
拖動這個分組以排序會把組名列表存入到視圖的”組名arr“數(shù)組里
當(dāng)打開右下角的”拖拽排序“開關(guān)時會觸發(fā)旁邊的掛載組件里的表達式,加載并配置SmoothDnD庫。 - 表格、列表
兩者類似,區(qū)別在于表格用table呈現(xiàn),把字段名展示在表格頭,列表用div呈現(xiàn),字段名和它的值放在一起展示。 - 日歷
日歷用的是FullCalendar插件,在掛載組件里的表達式里加載并配置。 - 甘特圖
下面專節(jié)講解 - 報表
有個掛載組件加載highcharts插件并初始化$v.任務(wù).狀態(tài)
和$v.任務(wù).任務(wù)類型
。
每個圖表都是直接渲染在各自的掛載組件$el
上面,在$obj.option
里定義其動態(tài)選項,在掛載事件里添加動態(tài)數(shù)據(jù)。
點擊每個數(shù)量都會彈窗展示其任務(wù)列表。
組件甘特圖
- 掛載時初始化
$v.甘特
move函數(shù):每調(diào)用一次都會在時間軸添加一周,傳入負值時就把時間軸往左延伸,軸首 - 7,正值便往右,軸尾 + 7,在隊首或隊尾推入新增的一周(包括年月日信息)。
日期計算比月份復(fù)雜點,我們以當(dāng)周的星期幾為基點,前面的日期推入隊首(unshift),后面的日期推入隊尾(push)。值得注意的是延伸過程中日期有可能是小于0或者大于本月最大天數(shù),那就意味著日期跨月份了,小了就要加上上月天數(shù),大了要減去本月天數(shù)。
月:也包含年份,用來渲染年月軸
日:用來渲染日期軸
首日:用來計算任務(wù)條和今日線(gantt-today-line)的left樣式 - 延伸時間軸
掛載時預(yù)先以今日為中心向前后各延伸了10周,同時在年月軸兩邊各放置了一個交叉觀察器,當(dāng)用戶滾到它那里時自動繼續(xù)延伸4周。$v.甘特.inited用來避免還未初始化就觸發(fā)滾動。 - hover
鼠標(biāo)挪到左邊固定欄的任務(wù)上時(onMouseOver)會把對應(yīng)的任務(wù)條滾入可視范圍(scrollIntoViewIfNeeded)。
鼠標(biāo)挪到任務(wù)條上時展示任務(wù)概要信息和前后拖拽手柄。 - 拖拽
拖動任務(wù)條本體和前后拖拽手柄都能夠更改任務(wù)的開始時間和(或)截止時間,同時動態(tài)顯示拖拽層(gantt-drag-mask)的可視化效果。
鼠標(biāo)按下時開始監(jiān)聽鼠標(biāo)移動事件,鼠標(biāo)松開時根據(jù)移動距離/偏移量修改開始時間和(或)截止時間。
拖拽任務(wù)條本體時如果未發(fā)生位移應(yīng)該認(rèn)為是點擊動作從而彈窗展示任務(wù)詳情
甘特圖任務(wù)
- 新建任務(wù)
A. 在看板、表格、列表類型的組件的右上角都有個“新建”按鈕用來新建無預(yù)設(shè)任務(wù);
B. 每個分組都有個加號按鈕用來添加同組新任務(wù):在$f.創(chuàng)建任務(wù)
的掛載事件中將把$v.modal.分組
賦給用來分組的字段中,如果該字段是狀態(tài)則進一步過濾出可能的狀態(tài)選項,有父任務(wù)類型時也同理;
C. 在任務(wù)彈窗中,如果允許有子任務(wù)類型,則可以在其某個子任務(wù)標(biāo)簽欄里新建子任務(wù),此時預(yù)設(shè)了父任務(wù)。 - 本組任務(wù)進度占比
每組任務(wù)上方都有個進度條,分別表示本組已完成、進行中、未開始的任務(wù)數(shù)及其占比。 - 任務(wù)彈窗頂部
由項目名、任務(wù)類型、父任務(wù)arr數(shù)組、當(dāng)前任務(wù)標(biāo)題及各自圖標(biāo)和顏色組成。
每節(jié)父任務(wù)可點擊展示其詳情。 - 分享到聊天
- 固定到任務(wù)欄
$v.任務(wù)欄
是當(dāng)前項目固定的任務(wù)列表,可展開收縮,可移除可點擊彈出詳情。 - 歸檔任務(wù)
- 刪除任務(wù)
同時也要刪除其關(guān)聯(lián)的一個任務(wù)活動和多個資源。
把所有關(guān)聯(lián)資源搜索出來,把返回來的arr克隆一下再依次刪除:
$r.arr.clone().forEach('$resource.delete($x._id)') - 標(biāo)題
點擊切換到編輯模式并聚焦,失去焦點或按下回車時保存修改并切換回展示模式。 - 當(dāng)前狀態(tài)
展示名稱、圖標(biāo)和顏色
點擊彈出當(dāng)前狀態(tài)下一個可流轉(zhuǎn)的狀態(tài)選項 - 負責(zé)人、開始時間、截止時間
- 基本信息
展示的是當(dāng)前任務(wù)類型的自定義屬性及其在此任務(wù)中的值。
分別根據(jù)各自的屬性類型來展示,通常分3列展示,但富文本和多行文本獨占一行。 - 子任務(wù)標(biāo)簽欄及其列表
可點擊展示,可解除關(guān)聯(lián)、刪除任務(wù),可變更負責(zé)人和截至日期 - 關(guān)聯(lián)任務(wù)
相關(guān)又非直接子任務(wù) - 附件
任務(wù)彈窗的頂層是個掛載組件,會把關(guān)聯(lián)資源搜索出來,雖然一開始不展示附件列表但也是要展示附件數(shù)量的。
添加,重命名,刪除
附件圖標(biāo)可能是上去前的本地文件預(yù)覽圖thumb,也可能是資源上傳后的縮略圖,還可能是根據(jù)其文件類型預(yù)設(shè)的文件圖標(biāo)。 - 評論
- 活動
因為用戶點擊的時候才展示,所以可以延緩加載,單獨存放于關(guān)聯(lián)xtk中,沒必要像評論一樣存于任務(wù)中。
任務(wù)日期選擇器
日期選擇器是比較常用的功能,也有點小復(fù)雜,本來可以選一個開源的js庫直接使用的,剛開始的時候為了驗證表達式的能力就直接寫了。
- 彈出日期選擇器前先初始化
$v.pop
:
keep:為保持點擊彈框本身不關(guān)框,參考$c.exp.onLogin里添加的一個全局click事件
date:當(dāng)前已設(shè)日期/時間,如果沒有就會是今天
time:是否需要設(shè)置時間,如果上面的date里包含了分鐘/秒鐘則默認(rèn)為真
F5:目的是再次觸發(fā)日期選擇器里的掛載事件從而重新初始化$v.日期
cb:選中/清除日期是調(diào)用的回調(diào)函數(shù),它會做“截止時間不能早于開始時間”的驗證后保存/清除對應(yīng)日期。 - 日期選擇器頂層是個掛載組件用以構(gòu)造
$v.日期
:
F5函數(shù):根據(jù)$v.日期里的年和月構(gòu)造那個月份的日期數(shù)組$v.日期.arr,每次掛載或年份、月份發(fā)生變化時都要調(diào)用。
日期數(shù)要比一個月的天數(shù)多,用的是6周,也即6 * 7的二維數(shù)組,所以有前頭有部分日期是上個月的,后頭有部分是下個月的。我們以本月第一個星期幾作為起點,小于它的是上個月的,月份 - 1,日期 + 上月天數(shù);大于本月天數(shù)的是下個月的,月份 + 1,日期 - 本月天數(shù)。
另外13月份是明年的1月,0月份是去年12月。 - 返回日期
如果需要設(shè)置時間則必須點“確定”按鈕才返回由年月日時分構(gòu)造的date,點選日期只是更新$v.日期里的月和日。
如果不需要設(shè)置時間點選日期則會立即返回由選中年月日構(gòu)造的date。 - 清除
如果需要設(shè)置時間“清除”按鈕只是關(guān)閉時間設(shè)置表單
如果不需要設(shè)置時間則會立即返回空以便調(diào)用者清除此日期 - 快速輸入時間
除了直接在輸入框直接輸入小時分鐘數(shù)外可以點擊輸入框上方或下方的箭頭進行加減,甚至直接按鍵盤上的上下箭頭鍵進行快速加減。為提高效率,分鐘數(shù)是以5為單位進行加減的。
注意在加減過程中小時分鐘數(shù)超出范圍時的特殊處理。 - 加減年份/月份
頂部有雙箭頭加減年份,單箭頭圖標(biāo)可加減月份。注意月份可能會翻到去年或明年時的檢查。 - 快速選擇跨度大的年份/月份
當(dāng)需要選擇較遠的年份時用上面的箭頭一步步加減有點慢,比如選擇一個老人的出生年月日,此時點擊頂部中央的年月可以直接選擇月份,再點擊頂部中央的年份可以以20年為單位快速定位年份。
日期選擇器審批
設(shè)計審批表單、分條件設(shè)置流程、實時計算明細統(tǒng)計和審批人、審批流程展示、同意/拒絕/轉(zhuǎn)交/撤銷審批、評論和查閱記錄、即時消息通知審批人、發(fā)送到聊天。
表單設(shè)計
- 分組管理
- 模板管理
- 審批表單設(shè)計
表單設(shè)計器是個拖拉拽的可視化編輯器,本身就是個簡易版的零代碼應(yīng)用開發(fā)工具,包括三部分:可選控件、表單主體及表單和控件屬性設(shè)置。
屬性包括:屬性類型,屬性名,提示文字,是否必填等。
單選框、復(fù)選框、下拉列表有選項列表。
明細是個特殊控件容器,可包含其他控件。明細里面的數(shù)字類型的控件還可以有“是否參與明細統(tǒng)計”,“是否條件流程因素項”兩項屬性。
重點、難點:
- 拖拽使用的是Sortable.js。
列表之間拖拽的要點有相同的組名(group name),可選控件列表的組的拉取選項(group pull)用的是clone,表示被拽出列表時是克隆一份給表單主體而不是被拽離走了,同時組不可添加新控件(put: false)。group: { name: "group", pull: "clone", put: false }。
嵌套列表推薦配置:{ fallbackOnBody: true, swapThreshold: 0.65 }。
onSort函數(shù)要分是表單主體里控件間排序還是從可選控件里拉進來一個控件,拉進來時參數(shù)里有個pullMode是clone(對應(yīng)前面的拉取選項clone), - 把控件從一個位置拖到新的位置時,是先把它從原來的位置(oldIndex)移除,在插入到新位置(newIndex)
$f.x.屬性.splice($l.e.newIndex, 0, $f.x.屬性.splice($l.e.oldIndex, 1)[0]) - 單擊選中一個表單控件時配置控件屬性路徑:$v.審批表單.屬性 = "屬性." + $index;而明細里的控件屬性路徑則是:$v.審批表單.屬性 = "屬性." + $parent.$index + ".屬性." + $index,它們是被嵌套在明細控件內(nèi)的,為了不觸發(fā)外層事件用$event.stopPropagation()阻止事件傳播
編輯控件各屬性時添加onChange事件,即使是空事件也會隨著輸入或勾選而實時更新/(渲染)表單主體對應(yīng)控件從而有更好的編輯體驗。 - 控件有多種類型,每種類型的表現(xiàn)形式和屬性都不同,所以要根據(jù)不同的類型用渲染條件來控制顯示哪種類型的控件。大家可根據(jù)實際需求進一步豐富控件列表。
- 重新渲染
表單主體是用一個掛載組件包裝的,當(dāng)添加新控件或控件排序時給$v.F5屬性
一個新值使表單重新渲染。
明細里的屬性列表也如此,但使用另一個變量$v.F5明細
僅重新渲染明細的屬性。
表單設(shè)計定義審批流程
不同類型審批,審批流程及每個流程的審批人不同
- 自由流程
提交人可以自由選擇審批由幾步組成,每個階段需要誰來審批,靈活性比較強,適合復(fù)雜程度不高且審批流程不定的審批,但不利于審批流程的規(guī)范性。 - 固定流程
無論申請條件如何,審批流程都是固定,適合復(fù)雜程度不高但流程固定的審批。 - 分條件設(shè)置流程
最常用的審批流程,當(dāng)申請條件不同時,需要不同的審批流程和審批人,如:報銷金額低于5000時只需要1級主管審批即可,報銷金額在5000至10000時,需要添加2級主管審批,大于10000時則需要在添加boss審批。 - 審批知會人
審批中不需要某些成員參與審批流程,但需要讓TA知曉這個審批。
重點、難點:
- 部門欄是公司的組織架構(gòu),可按部門分級添加一個或多個審批人?!安块T主管”是個未確定的變量,會在發(fā)起審批時得到替換成具體人員。
- 分條件設(shè)置流程中可通過表達式定義某種條件下的審批流,如招聘人數(shù)或報銷金額越多需要參與審批的人也越多層級越高。注意條件的連續(xù)性,不要留下空擋。
定義審批流程發(fā)起審批
- 填寫申請表單
- 實時計算明細統(tǒng)計
- 實時計算審批人
獲取部門主管 - 提交審批
驗證表單必填項。
生成審批編號
重點、難點:
- 明細統(tǒng)計分為合計與總計
合計是某個待統(tǒng)計字段之和,總計是各待統(tǒng)計字段合計之和。
累加用:reduce("$acc + $x", 0) - 條件流程中根據(jù)前面編好的條件表達式($x.條件)從審批流程列表中找到符合條件的流程。一個要點是執(zhí)行條件表達式是要把申請表單內(nèi)容($f.申請)傳進去作為執(zhí)行環(huán)境;另一個要點是找到流程的審批人列表需要克隆一下賦給申請表單的待審批人,因為接下來的審批改成會更改此待審批人(如替換部門主管,轉(zhuǎn)交審批人)而我們并不想影響到原流程中的審批人。
$f.申請.待審批人 = 審批流程.find('exc($x.條件, $f.申請)').審批人.clone() - 頁面onReady里定義了“找部門主管”函數(shù),申請表單掛載時執(zhí)行,條件流程中輸入數(shù)字時也會執(zhí)行。
難點是遞歸調(diào)用“$exp.部門主管”,部門從上往下一級級遞歸尋找直到到達申請人所在部門時被stopIf()停止。每次遞歸都把當(dāng)前部門傳給表達式執(zhí)行環(huán)境。 - 審批編號是申請年月日+4位數(shù)的序列號。序列號是每天從1開始的唯一數(shù),每次申請都從數(shù)據(jù)庫增加($inc)1得到。
- 提交審批時從待審批人列表把第一個審批人移出來作為當(dāng)前審批人,并發(fā)送即時消息給TA。
審批展示
- 當(dāng)前狀態(tài)
- 申請人
- 審批詳情
各類型數(shù)據(jù)展示
明細及其合計、總計 - 審批流程
- 知會人
- 評論
- 查閱記錄
重點、難點:
- 分類報銷可以把審批詳情較全面地展示,它外層的是個table元素的數(shù)據(jù)組件,里面由4個tbody,但它控制的是第二個用來展示明細的數(shù)據(jù)組件,第一個tbody是空的,用來占位,第三個用來展示屬性列表,第四個放在最下面用來顯示總計。
值得玩味的是第二個用來展示明細的數(shù)據(jù)組件沒有配置HTML元素,即它本身并不會渲染成HTML元素,而是為了控制它第一個子元素的渲染,即循環(huán)展示明細列表(第二個子元素用來展示合計),而每個明細又有多項屬性,再次使用數(shù)據(jù)組件,而明細里還有數(shù)組型屬性要用到數(shù)據(jù)組件(比如上傳多個附件)。
這是個嵌套使用數(shù)據(jù)組件極致展示復(fù)雜數(shù)據(jù)的例子。 - 審批流程中的數(shù)據(jù)組件控制的是第二個li,由審批流、當(dāng)前審批人、待審批人合并得來,第一個li固定放申請人,不受數(shù)據(jù)組件控制。
這里的【審批流】是已經(jīng)參與審批動作的人,可能包含撤銷人(申請人自己)和被轉(zhuǎn)交人。每次動作都往審批流中推入相關(guān)信息:$push: { "x.審批流": { 誰: $c.me._id, 時間: date(), 動作, 意見 } } - 評論的input并不是用來輸入的,而是點擊時(onFocus時讓$v.modal.評論 = true)彈出含有textarea的評論表單,點擊其他地方能收回評論表單是因為在更高層的地方(modal-detail-main)把$v.modal.評論置空。
- 審批模態(tài)窗里有個掛載組件,非申請人每次打開審批模態(tài)窗時都往【審批查閱】添加一條查閱記錄,
- 為了減少審批的信息量,把審批評論和審批查閱都保存到關(guān)聯(lián)xtk表中,以審批._id作為key。在各類審批列表場景中都不讀取評論和審批查閱,打開審批模態(tài)窗時在上面的掛載組件里默認(rèn)讀取評論,但依舊不讀取審批查閱。
審批展示審批流程
- 同意審批
- 拒絕審批
- 轉(zhuǎn)交審批
- 撤銷審批
- 評論審批
- 查閱審批
審批流程審批通知
- 我申請的
- 我審批的
- 知會我的
- 即時消息通知
- 發(fā)送到聊天
重點、難點:
- 獲取最新數(shù)據(jù)/重新渲染
待審在不同的人之間流轉(zhuǎn),信息變化快速。除了希望等待“我”審批的申請用即時消息通知到“我“以外,還希望在審批頁面時能收到跟“我”相關(guān)的【待審批】申請。
待審批數(shù):在根節(jié)點有個掛載組件,用$v.F5
作為key表達式,每當(dāng)$v.F5變化時(提交、撤銷、同意、拒絕、轉(zhuǎn)交審批)都會重新拉取“我”相關(guān)的待審批數(shù)量
審批列表:審批列表的table用一個以$v.tab + $v.F6
為key表達式掛載組件包裝。當(dāng)在左側(cè)導(dǎo)航欄(我申請的、我審批的,知會我的)或右上角標(biāo)簽欄(待審批、已審批)切換時都會重新拉取最新審批列表。
關(guān)鍵詞:組織,實現(xiàn),工具,協(xié)作,項目