This commit is contained in:
qsh
2026-02-04 15:10:50 +08:00
parent d97a222637
commit 5f1ba629ab
9 changed files with 1103 additions and 169 deletions

View File

@@ -0,0 +1,341 @@
<template>
<view class="account-form-container">
<!-- 页面标题 -->
<view class="page-header">
<view class="header-left" @click="goBack">
<view class="back-icon"></view>
</view>
<view class="header-title">{{ isEdit ? '编辑账号' : '添加账号' }}</view>
<view class="header-right" @click="submitForm">
<view class="save-btn">保存</view>
</view>
</view>
<!-- 表单内容 -->
<view class="form-content">
<!-- 基本信息 -->
<view class="form-section">
<view class="section-title">基本信息</view>
<view class="form-item">
<view class="form-label">姓名</view>
<view class="form-control">
<input
v-model="form.nickname"
class="form-input"
placeholder="请输入姓名"
/>
</view>
</view>
<view class="form-item">
<view class="form-label">手机号</view>
<view class="form-control">
<input
v-model="form.mobile"
class="form-input"
maxlength="11"
placeholder="请输入手机号"
type="number"
/>
</view>
</view>
</view>
<!-- 菜单权限 -->
<view class="form-section">
<view class="section-title">菜单权限</view>
<view class="menu-list">
<!-- 使用tree-select组件 -->
<tree-select
:tree-data="menuList"
id-field="id"
name-field="name"
v-model="form.menuIds"
/>
</view>
</view>
<!-- 数据权限 -->
<view class="form-section">
<view class="section-title">数据权限</view>
<view class="data-scope-list">
<radio-group v-model="form.dataScope">
<view
v-for="scope in dataScopeOptions"
:key="scope.value"
class="data-scope-item"
>
<radio
:value="scope.value"
:checked="form.dataScope == scope.value"
/>
<view class="data-scope-label">
<view class="scope-name">{{ scope.label }}</view>
<view class="scope-desc">{{ scope.desc }}</view>
</view>
</view>
</radio-group>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, computed, onMounted } from "vue"
import { addAccount, updateAccount, getAccountInfo } from "@/api/account/index.js"
import { useUserStore }from "@/store/modules/user.js"
import treeSelect from "@/components/tree-select.vue"
const userStore = useUserStore()
// 路由参数
const id = ref('');
const isEdit = computed(() => !!id.value);
// 表单数据
const form = ref({
nickname: '',
mobile: '',
menuIds: [],
dataScope: '1' // 默认全部数据
});
// 菜单列表
const menuList = computed(() => userStore.userMenus);
// 数据权限选项
const dataScopeOptions = [
{ value: '1', label: '全部数据', desc: '可以查看和管理所有数据' },
{ value: '5', label: '个人数据', desc: '只能查看和管理自己及下级数据' },
];
// 页面加载时获取参数
onMounted(() => {
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
id.value = currentPage.options.id || '';
if (isEdit.value) {
// 编辑模式,加载账号数据
loadAccountData();
}
});
// 加载账号数据
function loadAccountData() {
getAccountInfo(id.value).then(res => {
if (res.code == '0000') {
form.value = res.data;
} else {
uni.showToast({ title: res.msg || '加载账号数据失败', icon: 'none' });
}
});
}
// 提交表单
function submitForm() {
// 表单验证
if (!form.value.nickname) {
uni.showToast({ title: '请输入姓名', icon: 'none' });
return;
}
if (!form.value.mobile || form.value.mobile.length !== 11) {
uni.showToast({ title: '请输入有效的手机号', icon: 'none' });
return;
}
// 实际项目中应调用接口提交表单
if (isEdit.value) {
updateAccount(form.value).then(res => {
if (res.code == '0000') {
uni.showToast({ title: '保存成功', icon: 'success' });
setTimeout(() => {
goBack();
}, 1000);
} else {
uni.showToast({ title: res.msg || '保存失败', icon: 'none' });
}
});
} else {
addAccount(form.value).then(res => {
if (res.code == '0000') {
uni.showToast({ title: '添加成功', icon: 'success' });
setTimeout(() => {
goBack();
}, 1000);
} else {
uni.showToast({ title: res.msg || '添加失败', icon: 'none' });
}
});
}
}
// 返回上一页
function goBack() {
uni.navigateBack({ delta: 1 });
}
</script>
<style lang="scss" scoped>
/* #ifndef APP-NVUE */
page {
display: flex;
flex-direction: column;
box-sizing: border-box;
background-color: #f5f7fa;
min-height: 100%;
height: auto;
}
view {
font-size: 14px;
line-height: inherit;
}
/* #endif */
.account-form-container {
flex: 1;
display: flex;
flex-direction: column;
}
/* 页面头部 */
.page-header {
display: flex;
align-items: center;
justify-content: space-between;
height: 120rpx;
background-color: #fff;
padding: 0 32rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.header-left {
width: 60rpx;
}
.back-icon {
font-size: 40rpx;
color: #303133;
cursor: pointer;
}
.header-title {
font-size: 32rpx;
font-weight: bold;
color: #303133;
}
.header-right {
width: 100rpx;
text-align: right;
}
.save-btn {
font-size: 24rpx;
color: #409eff;
font-weight: 600;
cursor: pointer;
}
/* 表单内容 */
.form-content {
flex: 1;
padding: 24rpx;
}
/* 表单 section */
.form-section {
background-color: #fff;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 24rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
}
.section-title {
font-size: 28rpx;
font-weight: bold;
color: #303133;
margin-bottom: 24rpx;
}
/* 表单 item */
.form-item {
display: flex;
margin-bottom: 24rpx;
}
.form-label {
width: 120rpx;
font-size: 24rpx;
color: #606266;
display: flex;
align-items: center;
}
.form-control {
flex: 1;
background-color: #f9f9f9;
border: 1rpx solid #dcdfe6;
border-radius: 8rpx;
padding: 16rpx;
}
.form-input {
width: 100%;
font-size: 24rpx;
color: #303133;
}
/* 选择器 */
.picker {
display: flex;
justify-content: space-between;
align-items: center;
}
.picker-text {
font-size: 24rpx;
color: #303133;
}
/* 数据权限 */
.data-scope-list {
space-y: 16rpx;
}
.data-scope-item {
display: flex;
align-items: flex-start;
padding: 16rpx;
background-color: #f9f9f9;
border-radius: 8rpx;
margin-bottom: 16rpx;
}
.data-scope-label {
flex: 1;
margin-left: 16rpx;
}
.scope-name {
font-size: 24rpx;
color: #303133;
margin-bottom: 4rpx;
}
.scope-desc {
font-size: 20rpx;
color: #909399;
}
</style>

View File

@@ -7,7 +7,7 @@
</view>
<view class="header-title">账号创建与管控</view>
<view class="header-right" @click="addAccount">
<view class="add-btn">+ 添加</view>
<view class="add-btn">添加</view>
</view>
</view>
@@ -15,25 +15,23 @@
<view class="filter-section">
<view class="filter-row">
<view class="filter-item">
<view class="filter-label">账号状态</view>
<view class="filter-label">姓名</view>
<view class="filter-control">
<picker
:range="statusOptions"
:value="statusIndex"
@change="onStatusChange"
class="picker"
>
<view class="picker-text">{{ statusOptions[statusIndex] }}</view>
</picker>
<input
v-model="nickname"
class="search-input"
placeholder="请输入姓名"
@input="onSearch"
/>
</view>
</view>
<view class="filter-item">
<view class="filter-label">搜索</view>
<view class="filter-label">手机号</view>
<view class="filter-control">
<input
v-model="searchKeyword"
v-model="mobile"
class="search-input"
placeholder="请输入账号名称或手机号"
placeholder="请输入手机号"
@input="onSearch"
/>
</view>
@@ -49,11 +47,11 @@
class="account-item"
>
<view class="account-info">
<view class="account-name">{{ account.name }}</view>
<view class="account-name">{{ account.nickname }}</view>
<view class="account-meta">
<view class="meta-item">{{ account.phone }}</view>
<view class="meta-item">{{ account.role }}</view>
<view class="meta-item status-{{ account.status }}">{{ account.statusText }}</view>
<view class="meta-item">{{ account.mobile }}</view>
<view class="meta-item">{{ account.isDistributor ? '分销员' : '' }}</view>
<view class="meta-item" :class="`status-${account.status}`">{{ ['启用', '禁用'][account.status] }}</view>
</view>
</view>
<view class="account-actions">
@@ -61,11 +59,10 @@
编辑
</view>
<view
class="action-btn"
:class="account.status === 'active' ? 'disable-btn' : 'enable-btn'"
@click="toggleAccountStatus(account.id, account.status)"
class="action-btn delete-btn"
@click="handleDelete(account.id)"
>
{{ account.status === 'active' ? '冻结' : '启用' }}
删除
</view>
</view>
</view>
@@ -76,52 +73,57 @@
<view class="empty-icon">👥</view>
<view class="empty-text">暂无账号数据</view>
</view>
<!-- 分页信息 -->
<view class="pagination-info" v-if="accountList.length > 0">
<view class="info-text"> {{ total }} 条记录</view>
<view class="page-controls">
<view class="page-btn" :disabled="pageNo <= 1" @click="changePage(pageNo - 1)">上一页</view>
<view class="page-info">{{ pageNo }} / {{ totalPages }}</view>
<view class="page-btn" :disabled="pageNo >= totalPages" @click="changePage(pageNo + 1)">下一页</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from "vue"
import { ref, onMounted, computed } from "vue"
import { getAccountPage, deleteAccount } from '@/api/account'
// 状态选项
const statusOptions = ['全部', '启用', '冻结']
const statusIndex = ref(0)
// 筛选条件
const nickname = ref('')
const mobile = ref('')
// 搜索关键词
const searchKeyword = ref('')
// 分页参数
const pageNo = ref(1)
const pageSize = ref(10)
const total = ref(0)
// 计算总页数
const totalPages = computed(() => {
return Math.ceil(total.value / pageSize.value)
})
// 账号列表
const accountList = ref([
{
id: 1,
name: '张教练',
phone: '138****1234',
role: '分销员',
status: 'active',
statusText: '启用'
},
{
id: 2,
name: '李教练',
phone: '139****5678',
role: '分销员',
status: 'active',
statusText: '启用'
},
{
id: 3,
name: '王教练',
phone: '137****9012',
role: '分销员',
status: 'inactive',
statusText: '冻结'
}
])
const accountList = ref([])
onMounted(() => {
// 实际项目中应从接口获取账号列表
// loadAccountList()
loadAccountList()
})
const loadAccountList = async () => {
const params = {
nickname: nickname.value,
mobile: mobile.value,
pageNo: pageNo.value,
pageSize: pageSize.value
}
const { data } = await getAccountPage(params)
accountList.value = data.list
total.value = data.total
}
// 返回上一页
function goBack() {
uni.navigateBack({ delta: 1 })
@@ -130,55 +132,59 @@
// 添加账号
function addAccount() {
uni.navigateTo({
url: '/pages/account/add'
url: '/pages/account/accountForm'
})
}
// 编辑账号
function editAccount(accountId) {
uni.navigateTo({
url: `/pages/account/edit?id=${accountId}`
url: `/pages/account/accountForm?id=${accountId}`
})
}
// 切换账号状态
function toggleAccountStatus(accountId, currentStatus) {
const newStatus = currentStatus === 'active' ? 'inactive' : 'active'
const newStatusText = newStatus === 'active' ? '启用' : '冻结'
// 删除账号
function handleDelete(accountId) {
uni.showModal({
title: '确认操作',
content: `确定要${newStatusText}该账号吗?`,
success: function(res) {
title: '确认删除',
content: '确定要删除该账号吗?',
success: async function(res) {
if (res.confirm) {
// 实际项目中应调用接口切换账号状态
const account = accountList.value.find(item => item.id === accountId)
if (account) {
account.status = newStatus
account.statusText = newStatusText
try {
await deleteAccount(accountId)
uni.showToast({
title: '删除成功',
icon: 'success'
})
// 删除成功后刷新账号列表
loadAccountList()
} catch (error) {
uni.showToast({
title: error.message || '删除失败',
icon: 'none'
})
}
uni.showToast({
title: `账号已${newStatusText}`,
icon: 'success'
})
}
}
})
}
// 状态变更
function onStatusChange(e) {
const value = e.detail.value
statusIndex.value = value
// 实际项目中应根据状态筛选账号列表
// filterAccountList()
}
// 搜索
function onSearch() {
// 重置页码
pageNo.value = 1
// 实际项目中应根据关键词搜索账号列表
// searchAccountList()
}
// 切换页码
function changePage(newPage) {
if (newPage >= 1 && newPage <= totalPages.value) {
pageNo.value = newPage
// 实际项目中应根据新页码加载账号列表
// loadAccountList()
}
}
</script>
<style lang="scss" scoped>
@@ -325,6 +331,7 @@
display: flex;
gap: 16rpx;
flex-wrap: wrap;
align-items: center;
}
.meta-item {
@@ -332,11 +339,11 @@
color: #606266;
}
.status-active {
.status-0 {
color: #67c23a;
}
.status-inactive {
.status-1 {
color: #909399;
}
@@ -358,16 +365,11 @@
color: #409eff;
}
.disable-btn {
.delete-btn {
background-color: #fef0f0;
color: #f56c6c;
}
.enable-btn {
background-color: #f0f9eb;
color: #67c23a;
}
/* 空状态 */
.empty-state {
flex: 1;
@@ -388,61 +390,56 @@
color: #909399;
}
/* 平板和大屏响应式 */
@media screen and (min-width: 768px) {
.account-manage-container {
max-width: 1000px;
margin: 0 auto;
width: 100%;
}
.filter-section {
margin: 0 32rpx 24rpx;
padding: 32rpx;
}
.account-list {
padding: 0 32rpx;
}
.account-item {
margin-bottom: 24rpx;
padding: 32rpx;
}
.filter-row {
gap: 32rpx;
}
.filter-label {
font-size: 26rpx;
}
.picker-text,
.search-input {
font-size: 26rpx;
}
.filter-control {
padding: 20rpx;
}
.account-name {
font-size: 32rpx;
}
.meta-item {
font-size: 22rpx;
}
.action-btn {
font-size: 22rpx;
padding: 12rpx 24rpx;
}
.add-btn {
font-size: 28rpx;
}
/* 分页信息样式 */
.pagination-info {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16rpx 32rpx;
background-color: #fff;
border-top: 1rpx solid #e4e7ed;
margin-top: 16rpx;
}
.info-text {
font-size: 20rpx;
color: #606266;
}
.page-controls {
display: flex;
align-items: center;
gap: 16rpx;
}
.page-btn {
padding: 6rpx 16rpx;
border: 1rpx solid #dcdfe6;
border-radius: 4rpx;
font-size: 20rpx;
color: #606266;
cursor: pointer;
transition: all 0.3s ease;
}
.page-btn:hover {
border-color: #409eff;
color: #409eff;
}
.page-btn[disabled] {
opacity: 0.5;
cursor: not-allowed;
}
.page-btn[disabled]:hover {
border-color: #dcdfe6;
color: #606266;
}
.page-info {
font-size: 20rpx;
color: #303133;
}
/* 大屏设备响应式 */