feat: 团队成员

This commit is contained in:
wangdan-fit2cloud 2023-10-20 18:48:16 +08:00
parent 558213db40
commit 7187056e95
7 changed files with 292 additions and 23 deletions

View File

@ -1,5 +1,5 @@
import { Result } from '@/request/Result'
import { get, post } from '@/request/index'
import { get, post, del } from '@/request/index'
import type { TeamMember, TeamMemberRequest } from '@/api/type/team'
// import type { Ref } from 'vue'
@ -16,11 +16,20 @@ const getTeamMember: () => Promise<Result<TeamMember[]>> = () => {
*
* @param { "username_or_email": "string" }
*/
const postCreatTeamMember: (request: TeamMemberRequest) => Promise<Result<boolean>> = (request) => {
return post(`${prefix}`, request)
const postCreatTeamMember: (body: TeamMemberRequest) => Promise<Result<boolean>> = (body) => {
return post(`${prefix}`, body)
}
/**
*
* @param member_id
*/
const delTeamMember: (member_id: String) => Promise<Result<boolean>> = (member_id) => {
return del(`${prefix}/${member_id}`)
}
export default {
getTeamMember,
postCreatTeamMember
postCreatTeamMember,
delTeamMember
}

View File

@ -3,6 +3,7 @@ import AppIcon from './icons/AppIcon.vue'
import LoginLayout from './login-layout/index.vue'
import LoginContainer from './login-container/index.vue'
import LayoutContent from './content-container/LayoutContent.vue'
import TagsInput from './tags-input/index.vue'
export default {
install(app: App) {
@ -10,5 +11,6 @@ export default {
app.component(LoginLayout.name, LoginLayout)
app.component(LoginContainer.name, LoginContainer)
app.component(LayoutContent.name, LayoutContent)
app.component(TagsInput.name, TagsInput)
}
}

View File

@ -0,0 +1,91 @@
<template>
<!-- 外层div -->
<div ref="InputTag" class="tags-input">
<div class="tags-container" v-if="tagsList.length">
<!-- 标签 -->
<el-tag
v-for="(item, index) in tagsList"
:key="index"
@close="removeTag(item)"
closable
class="mr-10"
>{{ item }}
</el-tag>
</div>
<!-- 输入框 -->
<el-input
:validate-event="false"
v-model="currentval"
:placeholder="tagsList.length == 0 ? placeholder : ''"
@keydown.enter="addTags"
/>
</div>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
defineOptions({ name: 'TagsInput' })
const props = defineProps({
tags: {
//
type: Array<String>,
default: () => []
},
tag: {
//
type: String,
default: ''
},
placeholder: {
type: String,
default: '请输入'
},
limit: {
//
type: Number,
default: -1
},
reg: {
type: String,
default: ''
}
})
const emit = defineEmits(['update:tags', 'update:tag'])
const currentval = ref('')
const tagsList = ref<String[]>([])
watch([tagsList, currentval], (val) => {
if (val[0]?.length > 0) {
emit('update:tags', val[0])
} else if (val[1]) {
emit('update:tag', val[1])
}
})
function addTags() {
const val = currentval.value.trim()
if (val) {
tagsList.value.push(val)
}
currentval.value = ''
}
function removeTag(tag: String) {
tagsList.value.splice(tagsList.value.indexOf(tag), 1)
}
</script>
<style lang="scss" scoped>
.tags-input {
width: 100%;
min-height: 70px;
border: 1px solid var(--el-border-color);
border-radius: var(--el-border-radius-base);
:deep(.el-input__wrapper) {
background: none !important;
box-shadow: none !important;
border-radius: 0 !important;
resize: none;
}
.tags-container {
padding: 0 6px;
}
}
</style>

View File

@ -150,7 +150,7 @@ export const put: (
data?: unknown,
loading?: NProgress | Ref<boolean>
) => Promise<Result<any>> = (url, params, data, loading) => {
return promise(request({ url: url, method: 'put', data, params }), loading)
return promise(request({ url: url, method: 'put', params, data }), loading)
}
/**
@ -166,7 +166,7 @@ export const del: (
data?: unknown,
loading?: NProgress | Ref<boolean>
) => Promise<Result<any>> = (url, params, data, loading) => {
return promise(request({ url: url, method: 'delete', data, params }), loading)
return promise(request({ url: url, method: 'delete', params, data }), loading)
}
export const exportExcel: (

View File

@ -14,6 +14,16 @@
--el-form-inline-content-width: 100%;
}
.el-dialog {
.dialog-sub-title {
color: var(--el-text-color-regular);
margin: 5px 0;
}
.el-dialog__body {
padding: 15px var(--el-dialog-padding-primary) 10px !important;
}
}
// 抽屉样式整体修改
.el-drawer {
.el-drawer__header {

View File

@ -0,0 +1,102 @@
<template>
<el-dialog
v-model="dialogVisible"
:close-on-press-escape="false"
:close-on-click-modal="false"
:destroy-on-close="true"
width="600"
>
<template #header="{ titleId, titleClass }">
<h4 :id="titleId" :class="titleClass">添加成员</h4>
<div class="dialog-sub-title">成员登录后可以访问到您授权的数据</div>
</template>
<el-form
ref="addMemberFormRef"
:model="memberForm"
label-position="top"
:rules="rules"
@submit.prevent
>
<el-form-item label="用户名/邮箱" prop="users">
<tags-input
v-model:tags="memberForm.users"
v-model:tag="memberForm.user"
placeholder="请输入成员的用户名或邮箱,若需添加多个成员请使用回车分割。"
/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click.prevent="dialogVisible = false"> 取消 </el-button>
<el-button type="primary" @click="submitMember(addMemberFormRef)"> 添加 </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
import { MsgSuccess } from '@/utils/message'
import TeamApi from '@/api/team'
const emit = defineEmits(['refresh'])
const dialogVisible = ref<boolean>(false)
const memberForm = ref({
users: [],
user: ''
})
const addMemberFormRef = ref<FormInstance>()
const loading = ref<boolean>(false)
const validateUsers = (rule: any, value: any, callback: any) => {
if (value?.length == 0 && !memberForm.value.user) {
callback(new Error('请输入用户名/邮箱'))
} else {
callback()
}
}
const rules = ref<FormRules>({
users: [{ type: 'array', validator: validateUsers }]
})
watch(dialogVisible, (bool) => {
if (!bool) {
memberForm.value = {
users: [],
user: ''
}
}
})
const open = () => {
dialogVisible.value = true
}
const submitMember = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid, fields) => {
if (valid) {
loading.value = true
const obj: any = {
username_or_email: memberForm.value.users?.length
? memberForm.value.users.toString()
: memberForm.value.user
}
TeamApi.postCreatTeamMember(obj).then(() => {
MsgSuccess('提交成功')
emit('refresh')
dialogVisible.value = false
})
} else {
console.log('error submit!')
}
})
}
defineExpose({ open, close })
</script>
<style lang="scss" scope></style>

View File

@ -4,35 +4,43 @@
<div class="team-member p-15 border-r">
<h3>团队成员</h3>
<div class="align-right">
<el-button type="primary" link
><AppIcon iconName="app-add-users" class="add-user-icon" />添加成员</el-button
>
<el-button type="primary" link @click="addMember">
<AppIcon iconName="app-add-users" class="add-user-icon" />添加成员
</el-button>
</div>
<div class="mt-10">
<el-input v-model="filterText" placeholder="请输入用户名搜索" suffix-icon="Search" />
</div>
<div class="member-list mt-10">
<div class="member-list mt-10" v-loading="loading">
<el-scrollbar>
<ul>
<template v-for="(item, index) in memberList" :key="index">
<li class="active border-b-light flex-between p-15">
<ul v-if="filterMember.length > 0">
<template v-for="(item, index) in filterMember" :key="index">
<li
@click="clickMemberHandle(item.id)"
:class="currentUser === item.id ? 'active' : ''"
class="border-b-light flex-between p-15 cursor"
>
<div>
<span>{{ item.username }}</span>
<el-tag class="ml-10" effect="dark">所有者</el-tag>
<span class="mr-10">{{ item.username }}</span>
<el-tag effect="dark" v-if="isManage(item.type)">所有者</el-tag>
<el-tag effect="dark" type="warning" v-else>用户</el-tag>
</div>
<el-dropdown trigger="click">
<el-dropdown trigger="click" v-if="!isManage(item.type)">
<span class="cursor">
<el-icon><MoreFilled /></el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>移除</el-dropdown-item>
<el-dropdown-item @click.stop="deleteMember(item.id)"
>移除</el-dropdown-item
>
</el-dropdown-menu>
</template>
</el-dropdown>
</li>
</template>
</ul>
<el-empty description="暂无数据" v-else />
</el-scrollbar>
</div>
</div>
@ -70,6 +78,7 @@
</div>
</div>
</div>
<CreateMemberDialog ref="CreateMemberRef" @refresh="refresh" />
</LayoutContent>
</template>
@ -77,11 +86,16 @@
import { onMounted, ref, watch, nextTick } from 'vue'
import TeamApi from '@/api/team'
import type { TeamMember } from '@/api/type/team'
import CreateMemberDialog from './component/CreateMemberDialog.vue'
import { MsgSuccess } from '@/utils/message'
const CreateMemberRef = ref<InstanceType<typeof CreateMemberDialog>>()
const loading = ref(false)
const memberList = ref<TeamMember[]>([])
const memberList = ref<TeamMember[]>([]) //
const filterMember = ref<TeamMember[]>([]) //
const currentUser = ref<String>('')
const filterText = ref('')
const activeName = ref('dataset')
const allChecked = ref(false)
const tableHeight = ref(0)
@ -125,12 +139,53 @@ const tableData = [
}
]
watch(filterText, (val) => {
if (val) {
filterMember.value = memberList.value.filter((v) => v.username.includes(val))
} else {
filterMember.value = memberList.value
}
})
function deleteMember(id: String) {
loading.value = true
TeamApi.delTeamMember(id)
.then(() => {
MsgSuccess('删除成功')
getMember()
})
.catch(() => {
loading.value = false
})
}
function isManage(type: String) {
return type === 'manage'
}
function clickMemberHandle(id: String) {
currentUser.value = id
}
function addMember() {
CreateMemberRef.value?.open()
}
function getMember() {
loading.value = true
TeamApi.getTeamMember().then((res) => {
memberList.value = res.data
loading.value = false
})
TeamApi.getTeamMember()
.then((res) => {
memberList.value = res.data
filterMember.value = res.data
currentUser.value = memberList.value[0].id
loading.value = false
})
.catch(() => {
loading.value = false
})
}
function refresh() {
getMember()
}
onMounted(() => {