項目背景
1、vue者甲、bootstrap
2砌创、httprunner嫩实、celery、django-rest-framework
bootstrap開發(fā)過程中始終覺得沒有element ui好用宰缤。
Dashboard
image.png
Home
-
統(tǒng)計個人平臺使用記錄晃洒,支持月球及、日篩選
image.png
TestCaseList
image.png
拖拽步驟測試-支持列表項順序拖拽吃引、同步或異步運行測試
異步: 異步任務集合,執(zhí)行完成后返回測試報告
-
同步: 實時獲取結果
image.png
image.png -
swagger導入API
image.png
調試API
- 函數助手
- 基于Hook表達式編寫痛點。利用前端緩存實現表單輸入過濾提示函數
-
一鍵拖拽函數表達式
image.png - 代碼片段
<div class="hook-table" v-for="(row, index1) in tableList" :key="index1">
<input type="text" :name="`func${index1}`" v-model="row.func"
:key="`func${index1}`"
v-validate="{ required: true, min:2,max:50, regex: /^[-.${}()_a-zA-Z0-9]+$/ }"
:class="errors.first(`func${index1}`) || !hook.func ? 'form-control is-invalid' : 'form-control is-valid'"
placeholder="Please enter func" style="height: 35px;" @focus="closeSearch(index1)" @blur="removeClass(index1)">
<div :class="`searchbox-detail`" :id="`searchbox-detail-${index1}`" style="border: 1px solid #ced4da;">
<ul class="m-0 p-3">
<template v-if="filteredList(row.func).length > 0">
<li v-for="(item,index2) in filteredList(row.func)" :key="index2" class="iq-bg-primary-hover rounded">
<span @click="selectSearch(index, item.function_name)">{{ item.function_name }} | {{item.description}}</span>
</li>
</template>
<template v-else>
<li>Search Not Found</li>
</template>
</ul>
</div>
</div>
- @focus input 聚焦時,顯示下拉框
- @blur input 失去焦點時颅筋,關閉下拉框
methods: {
openSearch (index) {
// showSearch
if (document.getElementById(`searchbox-detail-${index}`) !== null) {
document.getElementById(`searchbox-detail-${index}`).classList.add('show-data')
}
},
closeSearch (index) {
if (document.getElementById(`searchbox-detail-${index}`) !== null && document.getElementById(`searchbox-detail-${index}`) !== undefined) {
// @blur權重問題使click無法事件觸發(fā)输枯,設置時間等待
setTimeout(() => {
document.getElementById(`searchbox-detail-${index}`).classList.remove('show-data')
}, 100)
}
},
}
- 計算屬性-過濾輸入字符串
computed: {
filteredList () {
return function (search) {
return this.hookList.filter(item => {
return item.function_name.toLowerCase().includes(search.toLowerCase())
})
}
}
},
-
HttpRequestBody-支持表單和文件上傳
image.png -
函數Diff monaco插件實現代碼行內比較功能
image.png 驗證器
-
Response- 支持一鍵復制Json表達式
image.png
測試報告詳情(部分設計參考pytestAllure)
image.png
image.png
用戶中心
image.png
消息功能
image.png
成員管理
image.png
權限組管理
- 后端權限;API視圖中間件實現用戶權限判斷
- 代碼片段
class CompanyAdminPermission(BasePermission):
"""公司管理員權限類"""
message = "僅公司管理員可訪問"
def has_permission(self, request, view):
# Return `True` if permission is granted, `False` otherwise.
if bool(request.user and request.user.is_authenticated):
company_user = request.user.companygroup_set.first()
if company_user.is_member:
return True
else:
self.message = "未提供身份校驗"
return False
- 前端權限厢汹;路由中間件實現權限判斷
- 代碼片段
router.beforeEach(async (to, from, next) => {
const publicPages = ['/auth/sign-in', '/auth/sign-up',]
if (publicPages.includes(to.path)) {
store.dispatch('User/resetAccessToken')
}
const authRequired = !publicPages.includes(to.path)
const loggedIn = store.getters['User/accessToken']
if (loggedIn) {
let permissions = store.getters['User/permissions']
if (!permissions.length) {
permissions = await store.dispatch('User/getUserInfo')
}
if (!hasPermission(permissions, to)) {
Alert('溫馨提示', '角色權限拒絕訪問烫葬,請聯(lián)系公司管理員', 'warning')
return false
}
}
if (to.meta.auth) {
if (authRequired && loggedIn === null) {
return next('/auth/sign-in')
} else if (to.name === 'dashboard' || to.name === 'mini.dashboard') {
return next('/home')
}
}
next()
})
image.png
...未完待續(xù)