505 lines
11 KiB
Vue
505 lines
11 KiB
Vue
<template>
|
||
<view class="page padding">
|
||
<!-- 成员管理卡片 -->
|
||
<view class="member-card bg-white radius">
|
||
<!-- 卡片头部:公司名称 + 角色标签 -->
|
||
<view class="card-header">
|
||
<view class="flex align-center">
|
||
<view class="border-line"></view>
|
||
<view class="text-bold margin-left-sm">{{ userInfo.deptName || '未知部门' }}</view>
|
||
</view>
|
||
<view class="role-tag">{{ roleText }}</view>
|
||
</view>
|
||
|
||
<!-- 成员列表 -->
|
||
<view class="member-list">
|
||
<view
|
||
class="member-item"
|
||
v-for="(item, index) in list"
|
||
:key="item.userId"
|
||
:class="{ 'border-bottom': index < list.length - 1 }"
|
||
>
|
||
<view class="cu-avatar radius lg bg-gray" style="background-image:url(https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png);"></view>
|
||
<view class="member-info">
|
||
<view class="flex align-center">
|
||
<text class="member-name">{{ item.nickName }}</text>
|
||
<view class="status-tag" :class="item.statusName === '正常' ? 'status-normal' : 'status-locked'">
|
||
{{ item.statusName }}
|
||
</view>
|
||
</view>
|
||
<view class="member-phone text-gray">
|
||
<text>手机:{{ item.phonenumber || '未设置' }}</text>
|
||
</view>
|
||
</view>
|
||
<button class="btn-lock bg-blue" @click="Lock(item)">
|
||
{{ item.status === '1' ? '解锁' : '锁定' }}
|
||
</button>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 添加成员按钮 -->
|
||
<view class="add-btn-wrapper">
|
||
<button class="add-btn" @click="showPopup = true">
|
||
<text class="cuIcon-add"></text>
|
||
<text>添加成员</text>
|
||
</button>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 添加成员弹出框 -->
|
||
<u-popup :show="showPopup" mode="center" round="20" @close="showPopup = false">
|
||
<view class="popup-content">
|
||
<view class="popup-header">
|
||
<view class="popup-title text-bold">添加成员</view>
|
||
<view class="popup-close" @click="showPopup = false">×</view>
|
||
</view>
|
||
|
||
<scroll-view class="popup-body" scroll-y>
|
||
<!-- 用户名 -->
|
||
<view class="form-item">
|
||
<view class="form-label">用户名<text class="text-red">*</text></view>
|
||
<up-input v-model="formData.username" placeholder="请输入用户名" border="surround"></up-input>
|
||
</view>
|
||
|
||
<!-- 昵称 -->
|
||
<view class="form-item">
|
||
<view class="form-label">昵称</view>
|
||
<up-input v-model="formData.nickname" placeholder="请输入昵称" border="surround"></up-input>
|
||
</view>
|
||
|
||
<!-- 手机号 -->
|
||
<view class="form-item">
|
||
<view class="form-label">手机号</view>
|
||
<up-input v-model="formData.phone" placeholder="请输入手机号" type="number" border="surround"></up-input>
|
||
</view>
|
||
|
||
<!-- 密码 -->
|
||
<view class="form-item">
|
||
<view class="form-label">密码<text class="text-red">*</text></view>
|
||
<up-input v-model="formData.password" placeholder="请输入密码(6-16位)" password border="surround"></up-input>
|
||
</view>
|
||
|
||
<!-- 角色类型 -->
|
||
<view class="form-item">
|
||
<view class="form-label">角色类型<text class="text-red">*</text></view>
|
||
<view class="form-select" @click="showRolePicker = true">
|
||
<text :class="selectedRoleName ? '' : 'text-gray'">
|
||
{{ selectedRoleName || '请选择角色类型' }}
|
||
</text>
|
||
<text class="cuIcon-unfold"></text>
|
||
</view>
|
||
<up-picker
|
||
:show="showRolePicker"
|
||
:columns="roleColumns"
|
||
@confirm="onRoleConfirm"
|
||
@cancel="showRolePicker = false"
|
||
@close="showRolePicker = false"
|
||
></up-picker>
|
||
</view>
|
||
</scroll-view>
|
||
|
||
<view class="popup-footer">
|
||
<button class="btn-cancel" @click="showPopup = false">取消</button>
|
||
<button class="btn-confirm bg-blue" @click="handleSubmit">确定</button>
|
||
</view>
|
||
</view>
|
||
</u-popup>
|
||
<TabBar />
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, reactive, computed, onMounted } from 'vue';
|
||
import { addMember, getMemberList, lockOrUnlockMember } from '@/request/api.js';
|
||
|
||
// 用户信息(从storage获取)
|
||
const userInfo = ref({
|
||
deptId: '',
|
||
deptName: '',
|
||
nickName: '',
|
||
role: '',
|
||
userId: '',
|
||
username: ''
|
||
});
|
||
|
||
// 角色显示文本
|
||
const roleText = computed(() => {
|
||
const role = userInfo.value.role;
|
||
if (role === 'manage' || role === 'admin') {
|
||
return '管理人员';
|
||
} else if (role === 'common') {
|
||
return '执行人员';
|
||
}
|
||
return '成员';
|
||
});
|
||
|
||
// 获取用户信息
|
||
const getUserInfo = () => {
|
||
try {
|
||
const userInfoStr = uni.getStorageSync('userInfo');
|
||
if (userInfoStr) {
|
||
userInfo.value = JSON.parse(userInfoStr);
|
||
console.log('用户信息:', userInfo.value);
|
||
}
|
||
} catch (error) {
|
||
console.error('获取用户信息失败:', error);
|
||
}
|
||
};
|
||
|
||
// 成员列表
|
||
const list = ref([]);
|
||
|
||
// 获取成员列表
|
||
const fetchMemberList = async () => {
|
||
try {
|
||
const res = await getMemberList();
|
||
if (res.code === 0 && res.data) {
|
||
list.value = res.data;
|
||
console.log('成员列表:', res.data);
|
||
}
|
||
} catch (error) {
|
||
console.error('获取成员列表失败:', error);
|
||
}
|
||
};
|
||
|
||
// 弹窗控制
|
||
const showPopup = ref(false);
|
||
const showRolePicker = ref(false);
|
||
const selectedRoleName = ref('');
|
||
|
||
// 表单数据
|
||
const formData = reactive({
|
||
username: '',
|
||
nickname: '',
|
||
phone: '',
|
||
password: '',
|
||
roleType: ''
|
||
});
|
||
|
||
// 角色类型选择器数据
|
||
const roleColumns = reactive([
|
||
['管理员', '普通成员']
|
||
]);
|
||
|
||
// 角色名称与值的映射
|
||
const roleMap = {
|
||
'管理员': 'manage',
|
||
'普通成员': 'common'
|
||
};
|
||
|
||
// 角色类型选择确认
|
||
const onRoleConfirm = (e) => {
|
||
if (e.value && e.value.length > 0) {
|
||
selectedRoleName.value = e.value[0];
|
||
formData.roleType = roleMap[e.value[0]];
|
||
}
|
||
showRolePicker.value = false;
|
||
};
|
||
|
||
// 重置表单
|
||
const resetForm = () => {
|
||
formData.username = '';
|
||
formData.nickname = '';
|
||
formData.phone = '';
|
||
formData.password = '';
|
||
formData.roleType = '';
|
||
selectedRoleName.value = '';
|
||
};
|
||
|
||
// 提交表单
|
||
const handleSubmit = async () => {
|
||
if (!formData.username) {
|
||
uni.showToast({ title: '请输入用户名', icon: 'none' });
|
||
return;
|
||
}
|
||
if (!formData.password || formData.password.length < 6 || formData.password.length > 16) {
|
||
uni.showToast({ title: '请输入6-16位密码', icon: 'none' });
|
||
return;
|
||
}
|
||
if (!formData.roleType) {
|
||
uni.showToast({ title: '请选择角色类型', icon: 'none' });
|
||
return;
|
||
}
|
||
|
||
const params = {
|
||
userName: formData.username,
|
||
nickName: formData.nickname || '',
|
||
phonenumber: formData.phone || '',
|
||
password: formData.password,
|
||
roleType: formData.roleType
|
||
};
|
||
|
||
try {
|
||
const res = await addMember(params);
|
||
if (res.code === 0) {
|
||
uni.showToast({ title: '添加成功', icon: 'success' });
|
||
showPopup.value = false;
|
||
resetForm();
|
||
// 刷新成员列表
|
||
fetchMemberList();
|
||
} else {
|
||
uni.showToast({ title: res.msg || '添加失败', icon: 'none' });
|
||
}
|
||
} catch (error) {
|
||
console.error('添加成员失败:', error);
|
||
uni.showToast({ title: '请求失败', icon: 'none' });
|
||
}
|
||
};
|
||
|
||
// 锁定/解锁成员
|
||
const Lock = (item) => {
|
||
const isLocked = item.status === '1';
|
||
const actionText = isLocked ? '解锁' : '锁定';
|
||
const newStatus = isLocked ? '0' : '1';
|
||
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: `确定要${actionText}该成员吗?`,
|
||
confirmColor: '#2667E9',
|
||
success: async (res) => {
|
||
if (res.confirm) {
|
||
try {
|
||
const result = await lockOrUnlockMember({
|
||
userId: item.userId,
|
||
lockStatus: Number(newStatus)
|
||
});
|
||
if (result.code === 0) {
|
||
uni.showToast({ title: `${actionText}成功`, icon: 'success' });
|
||
// 更新本地状态
|
||
item.status = newStatus;
|
||
item.statusName = newStatus === '1' ? '已锁定' : '正常';
|
||
} else {
|
||
uni.showToast({ title: result.msg || `${actionText}失败`, icon: 'none' });
|
||
}
|
||
} catch (error) {
|
||
console.error(`${actionText}成员失败:`, error);
|
||
uni.showToast({ title: '请求失败', icon: 'none' });
|
||
}
|
||
}
|
||
}
|
||
});
|
||
};
|
||
|
||
// 页面加载
|
||
onMounted(() => {
|
||
getUserInfo();
|
||
fetchMemberList();
|
||
});
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.page {
|
||
min-height: 100vh;
|
||
background: #EBF2FC;
|
||
}
|
||
|
||
// 成员卡片
|
||
.member-card {
|
||
padding: 0;
|
||
overflow: hidden;
|
||
}
|
||
|
||
// 卡片头部
|
||
.card-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 30rpx;
|
||
border-bottom: 1rpx solid #f0f0f0;
|
||
|
||
.border-line {
|
||
width: 8rpx;
|
||
height: 32rpx;
|
||
background: #2667E9;
|
||
border-radius: 8rpx;
|
||
}
|
||
}
|
||
|
||
// 角色标签
|
||
.role-tag {
|
||
padding: 8rpx 24rpx;
|
||
background: #EEF3FF;
|
||
color: #2667E9;
|
||
font-size: 24rpx;
|
||
border-radius: 24rpx 0 0 24rpx;
|
||
margin-right: -30rpx;
|
||
}
|
||
|
||
// 成员列表
|
||
.member-list {
|
||
padding: 0 30rpx;
|
||
}
|
||
|
||
// 成员项
|
||
.member-item {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 24rpx 0;
|
||
|
||
&.border-bottom {
|
||
border-bottom: 1rpx solid #f5f5f5;
|
||
}
|
||
|
||
.cu-avatar {
|
||
flex-shrink: 0;
|
||
}
|
||
}
|
||
|
||
// 成员信息
|
||
.member-info {
|
||
flex: 1;
|
||
margin-left: 20rpx;
|
||
overflow: hidden;
|
||
|
||
.member-name {
|
||
font-size: 30rpx;
|
||
color: #333;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.member-phone {
|
||
font-size: 24rpx;
|
||
margin-top: 8rpx;
|
||
}
|
||
}
|
||
|
||
// 状态标签
|
||
.status-tag {
|
||
margin-left: 12rpx;
|
||
padding: 4rpx 12rpx;
|
||
font-size: 22rpx;
|
||
border-radius: 6rpx;
|
||
|
||
&.status-normal {
|
||
background: #E8F5E9;
|
||
color: #4CAF50;
|
||
}
|
||
|
||
&.status-locked {
|
||
background: #FFEBEE;
|
||
color: #F44336;
|
||
}
|
||
}
|
||
|
||
// 锁定按钮
|
||
.btn-lock {
|
||
width: 120rpx;
|
||
height: 56rpx;
|
||
line-height: 56rpx;
|
||
padding: 0;
|
||
font-size: 26rpx;
|
||
border-radius: 28rpx;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
// 添加成员按钮
|
||
.add-btn-wrapper {
|
||
padding: 30rpx;
|
||
border-top: 1rpx solid #f0f0f0;
|
||
}
|
||
|
||
.add-btn {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 100%;
|
||
height: 88rpx;
|
||
background: #fff;
|
||
border: 2rpx dashed #2667E9;
|
||
border-radius: 12rpx;
|
||
color: #2667E9;
|
||
font-size: 30rpx;
|
||
|
||
.cuIcon-add {
|
||
margin-right: 10rpx;
|
||
font-size: 32rpx;
|
||
}
|
||
|
||
&::after {
|
||
border: none;
|
||
}
|
||
}
|
||
|
||
// 弹出框样式
|
||
.popup-content {
|
||
width: 600rpx;
|
||
background: #fff;
|
||
border-radius: 20rpx;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.popup-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 30rpx;
|
||
border-bottom: 1rpx solid #eee;
|
||
}
|
||
|
||
.popup-title {
|
||
font-size: 34rpx;
|
||
color: #333;
|
||
}
|
||
|
||
.popup-close {
|
||
font-size: 48rpx;
|
||
color: #999;
|
||
line-height: 1;
|
||
}
|
||
|
||
.popup-body {
|
||
padding: 30rpx;
|
||
max-height: 700rpx;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.form-item {
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.form-label {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
margin-bottom: 12rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.form-select {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
height: 80rpx;
|
||
border: 2rpx solid #dadbde;
|
||
border-radius: 8rpx;
|
||
padding: 0 24rpx;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
.popup-footer {
|
||
display: flex;
|
||
border-top: 1rpx solid #eee;
|
||
|
||
button {
|
||
flex: 1;
|
||
height: 90rpx;
|
||
line-height: 90rpx;
|
||
border-radius: 0;
|
||
font-size: 30rpx;
|
||
|
||
&::after {
|
||
border: none;
|
||
}
|
||
}
|
||
}
|
||
|
||
.btn-cancel {
|
||
background: #fff;
|
||
color: #666;
|
||
}
|
||
|
||
.btn-confirm {
|
||
color: #fff;
|
||
}
|
||
</style>
|