項(xiàng)目名稱:任務(wù)協(xié)作小程序
項(xiàng)目目標(biāo):開發(fā)一個(gè)基于微信小程序平臺(tái)的任務(wù)協(xié)作應(yīng)用,幫助團(tuán)隊(duì)成員更好地管理和跟蹤任務(wù),提高協(xié)作效率和質(zhì)量。
功能需求:
- 用戶管理
- 支持微信一鍵登錄,利用微信提供的登錄能力,簡化用戶注冊流程。
- 用戶與所屬機(jī)構(gòu)關(guān)聯(lián),同機(jī)構(gòu)用戶數(shù)據(jù)一致。
- 個(gè)人中心允許用戶查看和管理個(gè)人信息,提供頭像、昵稱等的修改功能。
- 任務(wù)看板
- 任務(wù)看板以列表和卡片的形式展示任務(wù),采用看板式設(shè)計(jì),任務(wù)狀態(tài)一目了然。可以拖拽移動(dòng)任務(wù)卡片,便于直觀地管理任務(wù)。
- 每個(gè)任務(wù)卡片設(shè)計(jì)醒目,包括標(biāo)題、負(fù)責(zé)人、截止日期等關(guān)鍵信息。
- 任務(wù)卡片可以設(shè)置優(yōu)先級(jí),用不同顏色或圖標(biāo)表示。
- 任務(wù)看板分為待辦、進(jìn)行中、已完成等多個(gè)列表,表示任務(wù)的不同狀態(tài)。
- 點(diǎn)擊任務(wù)卡片可以查看和編輯任務(wù)詳情,包括描述、子任務(wù)、附件、評論等。注意界面平滑切換,保證信息的一致性和流暢性。
- 任務(wù)可以多層級(jí)分解為子任務(wù),子任務(wù)完成后自動(dòng)更新父任務(wù)進(jìn)度,可以用進(jìn)度條等形式直觀展示。
- 任務(wù)支持協(xié)作討論和評論,可以@相關(guān)人員。
- 所有任務(wù)操作自動(dòng)記錄到值班日志,注意控制權(quán)限,如任務(wù)的查看和編輯權(quán)限。
- 值班日志
- 值班日志由任務(wù)操作、故障記錄、手動(dòng)輸入三部分組成,自動(dòng)歸類整理匯總。
- 字段包括但不限于日期、班次、人員、工作內(nèi)容等,可以考慮允許一定程度的自定義。
- 支持手動(dòng)填寫其他工作內(nèi)容。
- 可導(dǎo)出為格式化的 TXT 文件,文件名稱格式為"值班日志_日期_班次.txt"。導(dǎo)出時(shí)需要鑒權(quán),控制值班日志的查看和導(dǎo)出權(quán)限。
- 故障記錄
- 記錄故障發(fā)生時(shí)間、描述、影響范圍、原因等關(guān)鍵信息。
- 關(guān)聯(lián)到相應(yīng)任務(wù),形成閉環(huán)的故障處理流程。
- 自動(dòng)生成預(yù)防性維護(hù)任務(wù),提高系統(tǒng)可靠性。
- 關(guān)鍵信息自動(dòng)記入值班日志,與任務(wù)、日志形成聯(lián)動(dòng)。
- 提供故障統(tǒng)計(jì)分析功能,為運(yùn)維優(yōu)化提供數(shù)據(jù)支持??衫眯〕绦蛟崎_發(fā)的云函數(shù)和云數(shù)據(jù)庫能力。
- 文檔共享
- 以列表形式展示共享文檔。
- 點(diǎn)擊文檔,跳轉(zhuǎn)到騰訊文檔等在線文檔頁面。
- 注意文檔的鑒權(quán),只有相關(guān)人員可查看、編輯文檔。
- 通知提醒
- 新任務(wù)分配時(shí),自動(dòng)推送小程序通知,提醒相關(guān)用戶。
- 可以考慮提供多種提醒方式,如微信訂閱消息、郵件、短信等。
交互設(shè)計(jì)建議:
任務(wù)看板布局清晰,可拖拽操作,便于直觀管理任務(wù)。
2. 任務(wù)卡片設(shè)計(jì)醒目,關(guān)鍵信息一目了然,支持快速編輯。
3. 任務(wù)列表和任務(wù)詳情界面平滑切換,保證信息的一致性和流暢性。
4. 界面整體布局簡潔明了,突出重點(diǎn)信息。合理使用 tab、列表、卡片等組件。
5. 表單填寫、任務(wù)操作等提供引導(dǎo)和及時(shí)反饋,如填寫提示、完成提示、進(jìn)度展示等。
6. 注意權(quán)限控制,如任務(wù)的查看和編輯權(quán)限、值班日志的查看和導(dǎo)出權(quán)限等。
技術(shù)建議:
- 采用微信小程序云開發(fā)方案,利用其提供的云函數(shù)、云存儲(chǔ)、云數(shù)據(jù)庫等能力,加速開發(fā)進(jìn)程。
- 數(shù)據(jù)存儲(chǔ)可以考慮 JSON 格式,便于前端解析展示。存儲(chǔ)和獲取都需要進(jìn)行用戶鑒權(quán)。
- 導(dǎo)出功能可由前端生成 TXT 文件,然后以二進(jìn)制形式下載。后端提供數(shù)據(jù)接口支持。
- 評論、通知、導(dǎo)出等及時(shí)性要求高的功能,可以考慮合理利用緩存優(yōu)化性能。
- 故障記錄、統(tǒng)計(jì)分析等計(jì)算量大的功能,可以利用小程序云開發(fā)的云函數(shù)和云數(shù)據(jù)庫能力。
- 任務(wù)看板的拖拽操作需要合理控制,避免誤操作??梢钥紤]在拖拽時(shí)添加視覺反饋,如陰影、高亮等。
- 任務(wù)看板的性能優(yōu)化很重要,尤其是任務(wù)數(shù)量較多時(shí)。可以考慮分頁加載、懶加載等技術(shù)。
- 離線支持可以考慮利用小程序的本地存儲(chǔ),定期與服務(wù)器同步數(shù)據(jù),保證離線時(shí)也能使用基本功能。
安全與權(quán)限:
- 重視數(shù)據(jù)安全,涉及隱私的數(shù)據(jù)要加密存儲(chǔ),傳輸過程也要加密。
- 嚴(yán)格控制數(shù)據(jù)訪問權(quán)限,如任務(wù)的查看和編輯權(quán)限、值班日志的查看和導(dǎo)出權(quán)限等。
- 用戶登錄時(shí)進(jìn)行身份驗(yàn)證,保證只有合法用戶才能訪問數(shù)據(jù)。
- 定期備份數(shù)據(jù),制定數(shù)據(jù)恢復(fù)預(yù)案,最大限度減少數(shù)據(jù)丟失風(fēng)險(xiǎn)。
- 編寫安全代碼,防范常見的 Web 攻擊,如 XSS、CSRF、SQL 注入等。
其他建議:
- 提供完善的幫助文檔和用戶指南,方便用戶學(xué)習(xí)和使用。
- 建立用戶反饋渠道,收集用戶意見,不斷優(yōu)化產(chǎn)品。
- 制定合理的開發(fā)計(jì)劃和迭代策略,逐步完善功能,提高用戶滿意度。
- 注重代碼質(zhì)量,編寫清晰、易維護(hù)的代碼,必要時(shí)編寫單元測試,提高代碼可靠性。
- 密切關(guān)注小程序平臺(tái)的更新和變化,及時(shí)調(diào)整開發(fā)策略,充分利用新功能。
任務(wù)協(xié)作小程序設(shè)計(jì)了如下目錄結(jié)構(gòu):
│? app.js
│? app.json
│? app.wxss
│?
├─pages
│? ├─index
│? │????? index.js
│? │????? index.json
│? │????? index.wxml
│? │????? index.wxss
│? │?????
│? ├─login
│? │????? login.js
│? │????? login.json
│? │????? login.wxml
│? │????? login.wxss
│? │?????
│? ├─personal
│? │????? personal.js
│? │????? personal.json
│? │????? personal.wxml
│? │????? personal.wxss
│? │?????
│? ├─taskboard
│? │????? taskboard.js
│? │????? taskboard.json
│? │????? taskboard.wxml
│? │????? taskboard.wxss
│? │?????
│? ├─taskdetail
│? │????? taskdetail.js
│? │????? taskdetail.json
│? │????? taskdetail.wxml
│? │????? taskdetail.wxss
│? │?????
│? ├─dutylog
│? │????? dutylog.js
│? │????? dutylog.json
│? │????? dutylog.wxml
│? │????? dutylog.wxss
│? │?????
│? ├─faultrecord
│? │????? faultrecord.js
│? │????? faultrecord.json
│? │????? faultrecord.wxml
│? │????? faultrecord.wxss
│? │?????
│? └─doclist
│????????? doclist.js
│????????? doclist.json
│????????? doclist.wxml
│????????? doclist.wxss
│?????????
├─components
│? ├─taskcard
│? │????? taskcard.js
│? │????? taskcard.json
│? │????? taskcard.wxml
│? │????? taskcard.wxss
│? │?????
│? └─faultitem
│????????? faultitem.js
│????????? faultitem.json
│????????? faultitem.wxml
│????????? faultitem.wxss
│?????????
├─utils
│????? util.js
│????? auth.js
│????? request.js
│?????
├─services
│????? user.js
│????? task.js
│????? dutylog.js
│????? fault.js
│????? doc.js
│?????
└─cloud-functions
??? ├─login
??? │????? index.js
??? │????? package.json
??? │?????
??? ├─taskops
??? │????? index.js
??? │????? package.json
??? │?????
??? ├─faultops?
??? │????? index.js
??? │????? package.json
??? │?????
??? └─docops
??????????? index.js
??????????? package.json
app.js
// app.js
App({
?
onLaunch:
function
()
{
???
// 初始化云開發(fā)環(huán)境
??? wx
.cloud
.init({
?????
env:
'your-env-id',
?????
traceUser:
true,
???
})
?
},
?
globalData:
{
???
userInfo:
null
?
}
})
app.json
{
? "pages": [
??? "pages/index/index",
??? "pages/login/login",
??? "pages/personal/personal",
??? "pages/taskboard/taskboard",
??? "pages/taskdetail/taskdetail",
??? "pages/dutylog/dutylog",
??? "pages/faultrecord/faultrecord",
??? "pages/doclist/doclist"
? ],
? "window": {
??? "backgroundTextStyle": "light",
??? "navigationBarBackgroundColor": "#fff",
??? "navigationBarTitleText": "
任務(wù)協(xié)作
",
??? "navigationBarTextStyle": "black"
? },
? "sitemapLocation": "sitemap.json",
? "style": "v2"
}
pages/login/login.wxml
<!--login.wxml-->
<button open-type="getUserInfo" bindgetuserinfo="onGetUserInfo">微信登錄
</button>
pages/login/login.js
// login.js
Page({
?
onGetUserInfo:
function(e)
{
???
if
(!e
.detail
.userInfo
)
{
?????
// 用戶拒絕授權(quán)
?????
return;
???
}
???
// 執(zhí)行登錄,獲取用戶信息
??? wx
.cloud
.callFunction({
?????
name:
'login',
?????
data:
{},
?????
success:
res
=>
{
??????? console
.log('[云函數(shù)] [login] user openid: ', res
.result
.openid
)
??????? app
.globalData
.userInfo
= e
.detail
.userInfo
??????? app
.globalData
.openid
= res
.result
.openid
???????
// 由于 getUserInfo 是網(wǎng)絡(luò)請求,可能會(huì)在 Page.onLoad 之后才返回
???????
// 所以此處加入 callback 以防止這種情況
???????
if
(this.userInfoReadyCallback
)
{
?????????
this.userInfoReadyCallback(res
)
???????
}
?????
},
?????
fail:
err
=>
{
??????? console
.error('[云函數(shù)] [login] 調(diào)用失敗', err
)
?????
}
???
})
?
},
})
pages/taskboard/taskboard.wxml
<!--taskboard.wxml-->
<view class="container">
?
<view class="task-column">
???
<view class="task-column-title">待辦
</view>
???
<view wx:for="{{todoTasks}}" wx:key="id">
?????
<taskcard task="{{item}}"></taskcard>
???
</view>
?
</view>
?
<view class="task-column">
???
<view class="task-column-title">進(jìn)行中
</view>
???
<view wx:for="{{doingTasks}}" wx:key="id">
?????
<taskcard task="{{item}}"></taskcard>
???
</view>
?
</view>
?
<view class="task-column">
???
<view class="task-column-title">已完成
</view>
???
<view wx:for="{{doneTasks}}" wx:key="id">
?????
<taskcard task="{{item}}"></taskcard>
???
</view>
?
</view>
</view>
pages/taskboard/taskboard.js
// taskboard.js
const app
=
getApp()
Page({
?
data:
{
???
todoTasks:
[],
???
doingTasks:
[],
???
doneTasks:
[],
?
},
?
onLoad:
function()
{
???
this.getTasks()
?
},
?
getTasks:
function()
{ // 從云數(shù)據(jù)庫獲取任務(wù)數(shù)據(jù)
wx.cloud.callFunction({
name: 'taskops',
data: {
action: 'getTasks',
},
success: res => {
const tasks = res.result.data
this.setData({
todoTasks: tasks.filter(task => task.status === 'todo'),
doingTasks: tasks.filter(task => task.status === 'doing'),
doneTasks: tasks.filter(task => task.status === 'done'),
})
},
fail: err => {
console.error('[云函數(shù)] [taskops] 調(diào)用失敗', err)
}
})
},
})
components/taskcard/taskcard.wxml
```html
<!--taskcard.wxml-->
<view class="task-card" bindtap="openDetail" data-id="{{task._id}}">
? <view class="task-card-title">{{task.title}}</view>
? <view class="task-card-meta">
??? <text>{{task.assignee}}</text>
??? <text>{{task.dueDate}}</text>
? </view>
? <view class="task-card-priority" style="background-color: {{task.priority === 'high' ? 'red' : task.priority === 'middle' ? 'yellow' : 'green'}}"></view>
</view>
components/taskcard/taskcard.js
// taskcard.js
Component({
?
properties:
{
???
task:
{
?????
type: Object
,
?????
value:
{},
???
},
?
},
?
methods:
{
???
openDetail:
function(e)
{
?????
const taskId
= e
.currentTarget
.dataset
.id
????? wx
.navigateTo({
???????
url:
`/pages/taskdetail/taskdetail?id=${taskId}`,
?????
})
???
},
?
}
})
cloud-functions/taskops/index.js
// 云函數(shù)入口文件
const cloud
=
require('wx-server-sdk')
cloud
.init()
const db
= cloud
.database()
const _
= db
.command
// 云函數(shù)入口函數(shù)
exports
.main
=
async
(event, context)
=>
{
?
const
{ action
, taskId
, taskData
}
= event
?
const openid
= cloud
.getWXContext().OPENID
?
??
if
(action
===
'getTasks')
{
???
// 獲取任務(wù)列表
???
return
await db
.collection('tasks').where({
?????
assignee: openid
,
???
}).get()
?
}
else
if
(action
===
'addTask')
{
???
// 添加任務(wù)
???
return
await db
.collection('tasks').add({
?????
data:
{
???????
...taskData
,
???????
assignee: openid
,
???????
createTime: db
.serverDate(),
?????
}
???
})
?
}
else
if
(action
===
'updateTask')
{
???
// 更新任務(wù)
???
return
await db
.collection('tasks').doc(taskId
).update({
?????
data:
{
???????
...taskData
,
???????
updateTime: db
.serverDate(),
?????
},
???
})
?
}
?
// 其他操作...
}
pages/taskdetail/taskdetail.wxml
<!--taskdetail.wxml-->
<view class="container">
?
<view class="task-detail-title">{{task.title}}
</view>
?
<view class="task-detail-desc">{{task.description}}
</view>
?
<view class="task-detail-meta">
???
<text>負(fù)責(zé)人
: {{task.assignee}}
</text>
???
<text>截止時(shí)間
: {{task.dueDate}}
</text>
???
<text>優(yōu)先級(jí)
: {{task.priority}}
</text>
?
</view>
?
<view class="task-detail-subtasks">
???
<view class="task-detail-subtasks-title">子任務(wù)
</view>
???
<view wx:for="{{task.subtasks}}" wx:key="id">
?????
<view>{{item.title}}
</view>
?????
<view>{{item.status}}
</view>
???
</view>
?
</view>
?
<view class="task-detail-comments">
???
<view class="task-detail-comments-title">評論
</view>
???
<view wx:for="{{task.comments}}" wx:key="id">
?????
<view>{{item.author}}: {{item.content}}
</view>
???
</view>
???
<view>
?????
<input placeholder="添加評論" bindinput="onCommentInput"></input>
?????
<button bindtap="addComment">發(fā)送
</button>
???
</view>
?
</view>
?
<view class="task-detail-operations">
???
<button wx:if="{{task.status !== 'done'}}" bindtap="finishTask">完成任務(wù)
</button>
???
<button bindtap="deleteTask">刪除任務(wù)
</button>
?
</view>
</view>
pages/taskdetail/taskdetail.js
// taskdetail.js
Page({
?
data:
{
???
taskId:
'',
???
task:
{},
???
commentInput:
'',
?
},
?
onLoad:
function(options)
{
???
this.setData({
?????
taskId: options
.id
???
})
???
this.getTaskDetail()
?
},
?
getTaskDetail:
function()
{
??? wx
.cloud
.callFunction({
?????
name:
'taskops',
?????
data:
{
???????
action:
'getTaskDetail',
???????
taskId:
this.data
.taskId
,
?????
},
?????
success:
res
=>
{
???????
this.setData({
?????????
task: res
.result
.data
,
???????
})
?????
},
???
})
?
},
?
onCommentInput:
function(e)
{
???
this.setData({
?????
commentInput: e
.detail
.value
???
})
?
},
?
addComment:
function()
{
???
const comment
=
this.data
.commentInput
.trim()
???
if
(!comment
)
{
?????
return
???
}
??? wx
.cloud
.callFunction({
?????
name:
'taskops',
?????
data:
{
???????
action:
'addComment',
???????
taskId:
this.data
.taskId
,
???????
commentData:
{
?????????
content: comment
,
???????
},
?????
},
?????
success:
res
=>
{
???????
this.getTaskDetail()
???????
this.setData({
?????????
commentInput:
''
???????
})
?????
},
???
})
?
},
?
finishTask:
function()
{
??? wx
.cloud
.callFunction({
?????
name:
'taskops',
?????
data:
{
???????
action:
'updateTask',
???????
taskId:
this.data
.taskId
,
???????
taskData:
{
?????????
status:
'done',
???????
},
?????
},
?????
success:
res
=>
{
???????
this.getTaskDetail()
?????
},
???
})
?
},
?
deleteTask:
function()
{
??? wx
.cloud
.callFunction({
?????
name:
'taskops',
?????
data:
{
???????
action:
'deleteTask',
???????
taskId:
this.data
.taskId
,
?????
},
?????
success:
res
=>
{
??????? wx
.navigateBack()
?????
},
???
})
?
},
})
pages/dutylog/dutylog.wxml
<!--dutylog.wxml-->
<view class="container
">
pages/dutylog/dutylog.js
// dutylog.js
Page({
? data: {
??? dutyLogs: [],
? },
? onLoad: function() {
??? this.getDutyLogs()
? },
? getDutyLogs: function() {
??? wx.cloud.callFunction({
????? name: 'dutyops',
????? data: {
??????? action: 'getDutyLogs',
????? },
????? success: res => {
??????? this.setData({
????????? dutyLogs: res.result.data,
??????? })
????? },
??? })
? },
? exportLogs: function() {
??? wx.cloud.callFunction({
????? name: 'dutyops',
????? data: {
??????? action: 'exportDutyLogs',
????? },
????? success: res => {
??????? console.log(res.result)
??????? const fileUrl = res.result.fileUrl
??????? wx.downloadFile({
????????? url: fileUrl,
????????? success: res => {
??????????? const filePath = res.tempFilePath
??????????? wx.openDocument({
????????????? filePath: filePath,
??????????? })
????????? },
??????? })
????? },
??? })
? },
})
cloud-functions/dutyops/index.js
// 云函數(shù)入口文件
const cloud = require('wx-server-sdk')
cloud.init()
const db = cloud.database()
const _ = db.command
const xlsx = require('node-xlsx')
const fs = require('fs')
// 云函數(shù)入口函數(shù)
exports.main = async (event, context) => {
? const { action } = event
? if (action === 'getDutyLogs') {
??? // 獲取值班日志
??? return await db.collection('dutyLogs').get()
? } else if (action === 'exportDutyLogs') {
??? // 導(dǎo)出值班日志
??? const logs = (await db.collection('dutyLogs').get()).data
??? const data = logs.map(log => [log.date, log.shift, log.onDuty, log.content])
??? const headers = ['日期', '班次', '值班人員', '工作內(nèi)容']
??? const sheetData = [headers, ...data]
??? const sheet = xlsx.build([{name: '值班日志', data: sheetData}])
??? const buffer = sheet.toString('binary')
??? const today = new Date().toISOString().slice(0, 10)
??? const filename = `值班日志_${today}.xlsx`
??? const fileUrl = await cloud.uploadFile({
????? cloudPath: filename,
????? fileContent: Buffer.from(buffer, 'binary'),
??? })
??? return {
????? fileUrl: fileUrl.fileID
??? }
? }
}
以上分別是:
- 任務(wù)詳情頁面的布局和邏輯,包括任務(wù)信息展示、子任務(wù)列表、評論功能、完成和刪除任務(wù)等。
- 值班日志頁面的布局和邏輯,包括日志列表展示和日志導(dǎo)出功能。
- 值班日志相關(guān)的云函數(shù),包括獲取日志列表和導(dǎo)出日志到Excel等。
其中值班日志的導(dǎo)出用到了node-xlsx庫和云存儲(chǔ)的能力,將數(shù)據(jù)導(dǎo)出為Excel文件并上傳到云存儲(chǔ),返回文件地址供小程序端下載。
故障記錄和文檔共享的實(shí)現(xiàn)思路與任務(wù)和值班日志類似,這里就不再贅述。你可以參考上述代碼,結(jié)合具體的數(shù)據(jù)結(jié)構(gòu)和業(yè)務(wù)需求,自行實(shí)現(xiàn)相關(guān)功能。
除了主要的業(yè)務(wù)頁面和邏輯,小程序中還有一些其他的通用組件和工具類,例如:
components/faultitem/faultitem.wxml
<!--faultitem.wxml-->
<view class="fault-item">
?
<view class="fault-item-time">{{fault.time}}
</view>
?
<view class="fault-item-desc">{{fault.description}}
</view>
?
<view class="fault-item-impact">影響
: {{fault.impact}}
</view>
?
<view class="fault-item-operations">
???
<button bindtap="resolveFault" data-id="{{fault._id}}">解決
</button>
?
</view>
</view>
utils/util.js
// util.js
const
formatTime
=
date
=>
{
?
const year
= date
.getFullYear()
?
const month
= date
.getMonth()
+
1
?
const day
= date
.getDate()
?
const hour
= date
.getHours()
?
const minute
= date
.getMinutes()
?
const second
= date
.getSeconds()
?
return
[year
, month
, day
].map(formatNumber
).join('/')
+
' '
+
[hour
, minute
, second
].map(formatNumber
).join(':')
}
const
formatNumber
=
n
=>
{
? n
= n
.toString()
?
return n
[1]
? n
:
'0'
+ n
}
module
.exports
=
{
?
formatTime: formatTime
}
utils/auth.js
// auth.js
const app
=
getApp()
const
checkLogin
=
()
=>
{
?
return
new
Promise((resolve, reject)
=>
{
???
if
(app
.globalData
.userInfo
)
{
?????
resolve(app
.globalData
.userInfo
)
???
}
else
{
????? wx
.login({
???????
success:
()
=>
{
????????? wx
.getUserInfo({
???????????
success:
res
=>
{
????????????? app
.globalData
.userInfo
= res
.userInfo
?????????????
resolve(res
.userInfo
)
???????????
},
???????????
fail:
err
=>
{
?????????????
reject(err
)
???????????
}
?????????
})
???????
},
???????
fail:
err
=>
{
?????????
reject(err
)
???????
}
?????
})
???
}
?
})
}
const
checkPermission
=
(scope)
=>
{
?
return
new
Promise((resolve, reject)
=>
{
??? wx
.getSetting({
?????
success:
res
=>
{
???????
if
(res
.authSetting
[scope
])
{
?????????
resolve()
???????
}
else
{
????????? wx
.authorize({
???????????
scope: scope
,
???????????
success:
()
=>
{
?????????????
resolve()
???????????
},
???????????
fail:
err
=>
{
?????????????
reject(err
)
???????????
}
?????????
})
???????
}
?????
},
?????
fail:
err
=>
{
???????
reject(err
)
?????
}
???
})
?
})
}
module
.exports
=
{
?
checkLogin: checkLogin
,
?
checkPermission: checkPermission
,
}
這些工具類封裝了一些通用的函數(shù),如日期格式化、登錄檢查、權(quán)限檢查等,可以在項(xiàng)目中復(fù)用。
最后,項(xiàng)目中還應(yīng)該有一些配置文件,如:
project.config.json: 項(xiàng)目配置文件,包含了項(xiàng)目的一些元信息和設(shè)置。
sitemap.json: 配置小程序及其頁面是否允許被微信索引。
以上就是一個(gè)相對完整的任務(wù)協(xié)作小程序的主要代碼組成和結(jié)構(gòu),涵蓋了從前端頁面到后端云函數(shù)的主要功能模塊的實(shí)現(xiàn)。你可以在此基礎(chǔ)上進(jìn)一步完善和擴(kuò)展,開發(fā)出一個(gè)滿足實(shí)際需求的生產(chǎn)級(jí)別應(yīng)用。
pages/personal/personal.wxml
<!--personal.wxml-->
<view class="container">
?
<view class="personal-info">
???
<image class="personal-avatar" src="{{userInfo.avatarUrl}}"></image>
???
<text class="personal-nickname">{{userInfo.nickName}}
</text>
?
</view>
?
<view class="personal-stats">
???
<view class="personal-stats-item">
?????
<text class="personal-stats-num">{{todoCount}}
</text>
?????
<text class="personal-stats-label">待辦任務(wù)
</text>
???
</view>
???
<view class="personal-stats-item">
?????
<text class="personal-stats-num">{{doneCount}}
</text>
?????
<text class="personal-stats-label">已完成任務(wù)
</text>
???
</view>
?
</view>
?
<view class="personal-settings">
???
<view class="personal-settings-item">
?????
<text>修改頭像
</text>
?????
<button open-type="chooseAvatar" bind:chooseavatar="onChooseAvatar">選擇頭像
</button>
???
</view>
???
<view class="personal-settings-item">
?????
<text>修改昵稱
</text>
?????
<input value="{{userInfo.nickName}}" bindinput="onNicknameInput"></input>
?????
<button bindtap="updateNickname">保存
</button>
???
</view>
?
</view>
</view>
pages/personal/personal.js
// personal.js
const app
=
getApp()
Page({
?
data:
{
???
userInfo:
{},
???
todoCount:
0,
???
doneCount:
0,
?
},
?
onLoad()
{
???
this.setData({
?????
userInfo: app
.globalData
.userInfo
???
})
???
this.getTaskStats()
?
},
?
getTaskStats()
{
??? wx
.cloud
.callFunction({
?????
name:
'taskops',
?????
data:
{
???????
action:
'getTaskStats',
?????
},
?????
success:
res
=>
{
???????
const
{ todoCount
, doneCount
}
= res
.result
???????
this.setData({
????????? todoCount
,
????????? doneCount
,
???????
})
?????
},
???
})
?
},
?
onChooseAvatar(e)
{
???
const
{ avatarUrl
}
= e
.detail
????
this.setData({
?????
'userInfo.avatarUrl': avatarUrl
,
???
})
???
this.updateUserInfo()
?
},
?
onNicknameInput(e)
{
???
this.setData({
?????
'userInfo.nickName': e
.detail
.value
???
})
?
},
?
updateNickname()
{
???
this.updateUserInfo()
?
},
?
updateUserInfo()
{
??? wx
.cloud
.callFunction({
?????
name:
'userops',
?????
data:
{
???????
action:
'updateUserInfo',
???????
userInfo:
this.data
.userInfo
,
?????
},
?????
success:
res
=>
{
??????? app
.globalData
.userInfo
=
this.data
.userInfo
?????
},
???
})
?
},
})
cloud-functions/userops/index.js
// 云函數(shù)入口文件
const cloud
=
require('wx-server-sdk')
cloud
.init()
const db
= cloud
.database()
// 云函數(shù)入口函數(shù)
exports
.main
=
async
(event, context)
=>
{
?
const
{ action
, userInfo
}
= event
?
if
(action
===
'updateUserInfo')
{
???
const openid
= cloud
.getWXContext().OPENID
???
return
await db
.collection('users').where({
?????
_openid: openid
???
}).update({
?????
data: userInfo
???
})
?
}
}
services/user.js
// user.js
const app
=
getApp()
const
updateUserInfo
=
async
(userInfo)
=>
{
?
if
(!app
.globalData
.openid
)
{
???
await app
.getOpenidAsync()
?
}
?
const db
= wx
.cloud
.database()
?
return db
.collection('users').where({_openid: app.globalData.openid
}).update({
data: userInfo
})
}
const getUserInfo = async () => {
if (!app.globalData.openid) {
await app.getOpenidAsync()
}
const db = wx.cloud.database()
const res = await db.collection('users').where({
_openid: app.globalData.openid
}).get()
return res.data[0]
}
module.exports = {
updateUserInfo,
getUserInfo,
}
services/task.js
```javascript
// task.js
const db = wx.cloud.database()
const _ = db.command
const addTask = async (task) => {
? return db.collection('tasks').add({
??? data: {
????? ...task,
????? status: 'todo',
????? createTime: db.serverDate(),
??? }
? })
}
const updateTask = async (id, task) => {
? return db.collection('tasks').doc(id).update({
??? data: {
????? ...task,
????? updateTime: db.serverDate(),
??? }
? })
}
const deleteTask = async (id) => {
? return db.collection('tasks').doc(id).remove()
}
const getTasks = async (status) => {
? return db.collection('tasks').where({
??? status,
? }).get()
}
const getTaskStats = async (openid) => {
? const todoRes = await db.collection('tasks').where({
??? _openid: openid,
??? status: 'todo',
? }).count()
? const doneRes = await db.collection('tasks').where({
??? _openid: openid,
??? status: 'done',
? }).count()
? return {
??? todoCount: todoRes.total,
??? doneCount: doneRes.total,
? }
}
module.exports = {
? addTask,
? updateTask,
? deleteTask,
? getTasks,
? getTaskStats,
}
app.js
// app.js
App({
?
globalData:
{
???
userInfo:
null,
???
openid:
null,
?
},
?
onLaunch()
{
???
this.initCloud()
?
},
?
initCloud()
{
??? wx
.cloud
.init({
?????
env:
'your-env-id',
???
})
?
},
?
getOpenidAsync()
{
???
return
new
Promise((resolve, reject)
=>
{
????? wx
.cloud
.callFunction({
???????
name:
'login',
?????
}).then(res
=>
{
???????
this.globalData
.openid
= res
.result
.openid
???????
resolve(res
.result
.openid
)
?????
}).catch(err
=>
{
??????? console
.error('getOpenidAsync error:', err
)
???????
reject(err
)
?????
})
???
})
?
},
})
pages/faultrecord/faultrecord.wxml
<!--faultrecord.wxml-->
<view class="container">
?
<view class="fault-list">
???
<view wx:for="{{faults}}" wx:key="id">
?????
<faultitem fault="{{item}}"></faultitem>
???
</view>
?
</view>
?
<view class="fault-add">
???
<button bindtap="addFault">添加故障
</button>
?
</view>
</view>
pages/faultrecord/faultrecord.js
// faultrecord.js
const
{ getFaults
, addFault
, resolveFault
}
=
require('../../services/fault')
Page({
?
data:
{
???
faults:
[],
?
},
?
onLoad()
{
???
this.getFaultList()
?
},
?
async
getFaultList()
{
???
const faults
=
await
getFaults()
???
this.setData({ faults
})
?
},
?
addFault()
{
??? wx
.navigateTo({
?????
url:
'/pages/faultdetail/faultdetail',
???
})
?
},
?
async
handleResolveFault(e)
{
???
const
{ id
}
= e
.detail
???
await
resolveFault(id
)
???
this.getFaultList()
?
},
})
pages/faultdetail/faultdetail.wxml
<!--faultdetail.wxml-->
<view class="container">
?
<view class="fault-detail-form">
???
<view class="fault-detail-form-item">
?????
<text>故障時(shí)間
:
</text>
?????
<picker mode="date" value="{{fault.time}}" bindchange="onTimeChange">
???????
<view>{{fault.time}}
</view>
?????
</picker>
???
</view>
???
<view class="fault-detail-form-item">
?????
<text>故障描述
:
</text>
?????
<textarea value="{{fault.description}}" bindinput="onDescInput"></textarea>
???
</view>
???
<view class="fault-detail-form-item">
?????
<text>影響范圍
:
</text>
?????
<input value="{{fault.impact}}" bindinput="onImpactInput"></input>
???
</view>
?
</view>
?
<view class="fault-detail-actions">
???
<button bindtap="saveFault">保存
</button>
?
</view>
</view>
pages/faultdetail/faultdetail.js
// faultdetail.js
const
{ addFault
}
=
require('../../services/fault')
Page({
?
data:
{
???
fault:
{
?????
time:
'',
?????
description:
'',
?????
impact:
'',
???
},
?
},
?
onLoad(options)
{
???
if
(options
.id
)
{
?????
// 編輯模式
?????
// 獲取故障詳情
???
}
else
{
?????
// 新增模式
?????
this.setData({
???????
fault:
{
?????????
time:
this.formatDate(new
Date()),
?????????
description:
'',
?????????
impact:
'',
???????
},
?????
})
???
}
?
},
?
formatDate(date)
{
???
const year
= date
.getFullYear()
???
const month
= date
.getMonth()
+
1
???
const day
= date
.getDate()
???
return
`${year}-${month}-${day}`
?
},
?
onTimeChange(e)
{
???
this.setData({
?????
'fault.time': e
.detail
.value
,
???
})
?
},
?
onDescInput(e)
{
???
this.setData({
?????
'fault.description': e
.detail
.value
,
???
})
?
},
?
onImpactInput(e)
{
???
this.setData({
?????
'fault.impact': e
.detail
.value
,
???
})
?
},
?
async
saveFault()
{
???
await
addFault(this.data
.fault
)
??? wx
.navigateBack()
?
},
})
pages/doclist/doclist.wxml
<!--doclist
.wxml-->
pages/doclist/doclist.js
```javascript
// doclist.js
const { getDocs } = require('../../services/doc')
Page({
? data: {
??? docs: [],
? },
? onLoad() {
??? this.getDocList()
? },
? async getDocList() {
??? const docs = await getDocs()
??? this.setData({ docs })
? },
? openDoc(e) {
??? const { url } = e.currentTarget.dataset
??? wx.navigateTo({
????? url: `/pages/webview/webview?url=${url}`,
??? })
? },
})
pages/webview/webview.wxml
<!--webview.wxml-->
<web-view src="{{url}}"></web-view>
pages/webview/webview.js
// webview.js
Page({
?
data:
{
???
url:
'',
?
},
?
onLoad(options)
{
???
const
{ url
}
= options
???
this.setData({ url
})
?
},
})
services/fault.js
// fault.js
const db
= wx
.cloud
.database()
const
addFault
=
async
(fault)
=>
{
?
return db
.collection('faults').add({
???
data:
{
?????
...fault
,
?????
status:
'unsolved',
?????
createTime: db
.serverDate(),
???
}
?
})
}
const
getFaults
=
async
()
=>
{
?
return db
.collection('faults').orderBy('createTime',
'desc').get()
}
const
resolveFault
=
async
(id)
=>
{
?
return db
.collection('faults').doc(id
).update({
???
data:
{
?????
status:
'resolved',
?????
resolveTime: db
.serverDate(),
???
}
?
})
}
module
.exports
=
{
? addFault
,
? getFaults
,
? resolveFault
,
}
services/doc.js
// doc.js
const db
= wx
.cloud
.database()
const
addDoc
=
async
(doc)
=>
{
?
return db
.collection('docs').add({
???
data:
{
?????
...doc
,
?????
createTime: db
.serverDate(),
?????
updateTime: db
.serverDate(),
???
}
?
})
}
const
getDocs
=
async
()
=>
{
?
return db
.collection('docs').orderBy('updateTime',
'desc').get()
}
module
.exports
=
{
? addDoc
,
? getDocs
,
}
cloud-functions/faultops/index.js
// 云函數(shù)入口文件
const cloud
=
require('wx-server-sdk')
cloud
.init()
const db
= cloud
.database()
const _
= db
.command
// 云函數(shù)入口函數(shù)
exports
.main
=
async
(event, context)
=>
{
?
const
{ action
, faultId
, faultData
}
= event
?
if
(action
===
'addFault')
{
???
return
await db
.collection('faults').add({
?????
data:
{
???????
...faultData
,
???????
status:
'unsolved',
???????
createTime: db
.serverDate(),
?????
}
???
})
?
}
else
if
(action
===
'resolveFault')
{
???
return
await db
.collection('faults').doc(faultId
).update({
?????
data:
{
???????
status:
'resolved',
???????
resolveTime: db
.serverDate(),
?????
}
???
})
?
}
}
cloud-functions/docops/index.js
// 云函數(shù)入口文件
const cloud
=
require('wx-server-sdk')
cloud
.init()
const db
= cloud
.database()
const _
= db
.command
// 云函數(shù)入口函數(shù)
exports.main = async (event, context) => {
const { action, docData } = event文章來源:http://www.zghlxwxcb.cn/news/detail-850283.html
if (action === 'addDoc') {
return await db.collection('docs').add({
data: {
...docData,
createTime: db.serverDate(),
updateTime: db.serverDate(),
}
})
}
}文章來源地址http://www.zghlxwxcb.cn/news/detail-850283.html
到了這里,關(guān)于任務(wù)協(xié)作小程序的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!