mirror of
https://gitee.com/ulthon/ulthon_admin.git
synced 2026-07-01 15:32:48 +08:00
feat: 发布智能体版
This commit is contained in:
57
source/clients/uniapp/src/api/auth.js
Normal file
57
source/clients/uniapp/src/api/auth.js
Normal file
@@ -0,0 +1,57 @@
|
||||
import { request } from '../utils/request'
|
||||
|
||||
/**
|
||||
* 登录
|
||||
* @param {Object} data - 登录参数
|
||||
* @param {string} data.username - 用户名
|
||||
* @param {string} data.password - 密码
|
||||
* @param {string} [data.captcha] - 验证码
|
||||
* @param {number} [data.keep_login] - 是否保持登录
|
||||
* @returns {Promise<Object>} 登录响应
|
||||
*/
|
||||
export function login(data) {
|
||||
return request({
|
||||
url: '/admin/login/index',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出登录
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
export function logout() {
|
||||
return request({
|
||||
url: '/admin/login/out',
|
||||
method: 'POST',
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户信息
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
export function getProfile() {
|
||||
return request({
|
||||
url: '/admin/index/editAdmin',
|
||||
method: 'GET',
|
||||
data: { get_page_data: 1 },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
* @param {Object} data - 修改密码参数
|
||||
* @param {string} data.old_password - 旧密码
|
||||
* @param {string} data.new_password - 新密码
|
||||
* @param {string} data.confirm_password - 确认密码
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
export function changePassword(data) {
|
||||
return request({
|
||||
url: '/admin/index/changePassword',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
101
source/clients/uniapp/src/api/permission.js
Normal file
101
source/clients/uniapp/src/api/permission.js
Normal file
@@ -0,0 +1,101 @@
|
||||
import { request } from '../utils/request'
|
||||
|
||||
/**
|
||||
* 获取权限列表(树形)
|
||||
* @param {Object} params - 查询参数
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
export function getPermissionTree(params = {}) {
|
||||
return request({
|
||||
url: '/admin/auth/index',
|
||||
method: 'GET',
|
||||
data: params,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取权限节点列表
|
||||
* @param {Object} params - 查询参数
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
export function getPermissionList(params = {}) {
|
||||
return request({
|
||||
url: '/admin/auth/index',
|
||||
method: 'GET',
|
||||
data: params,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取权限详情
|
||||
* @param {number} id - 权限ID
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
export function getPermissionDetail(id) {
|
||||
return request({
|
||||
url: '/admin/auth/edit',
|
||||
method: 'GET',
|
||||
data: { id },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建权限
|
||||
* @param {Object} data - 权限数据
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
export function createPermission(data) {
|
||||
return request({
|
||||
url: '/admin/auth/edit',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新权限
|
||||
* @param {Object} data - 权限数据
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
export function updatePermission(data) {
|
||||
return request({
|
||||
url: '/admin/auth/edit',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除权限
|
||||
* @param {number} id - 权限ID
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
export function deletePermission(id) {
|
||||
return request({
|
||||
url: '/admin/auth/delete',
|
||||
method: 'POST',
|
||||
data: { id },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新权限节点(同步后端权限到数据库)
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
export function syncPermissions() {
|
||||
return request({
|
||||
url: '/admin/auth/updateAuth',
|
||||
method: 'POST',
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户权限列表
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
export function getUserPermissions() {
|
||||
return request({
|
||||
url: '/admin/auth/userAuth',
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
130
source/clients/uniapp/src/api/user.js
Normal file
130
source/clients/uniapp/src/api/user.js
Normal file
@@ -0,0 +1,130 @@
|
||||
import { request } from '../utils/request'
|
||||
|
||||
/**
|
||||
* 获取用户列表
|
||||
* @param {Object} params - 查询参数
|
||||
* @param {number} [params.page] - 页码
|
||||
* @param {number} [params.limit] - 每页数量
|
||||
* @param {string} [params.username] - 用户名
|
||||
* @param {string} [params.nickname] - 昵称
|
||||
* @param {string} [params.phone] - 手机号
|
||||
* @param {number} [params.status] - 状态
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
export function getUserList(params = {}) {
|
||||
return request({
|
||||
url: '/admin/admin/index',
|
||||
method: 'GET',
|
||||
data: params,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户详情
|
||||
* @param {number} id - 用户ID
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
export function getUserDetail(id) {
|
||||
return request({
|
||||
url: '/admin/admin/edit',
|
||||
method: 'GET',
|
||||
data: { id },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建用户
|
||||
* @param {Object} data - 用户数据
|
||||
* @param {string} data.username - 用户名
|
||||
* @param {string} data.password - 密码
|
||||
* @param {string} [data.nickname] - 昵称
|
||||
* @param {string} [data.phone] - 手机号
|
||||
* @param {string} [data.email] - 邮箱
|
||||
* @param {string} [data.head_img] - 头像
|
||||
* @param {string} [data.remark] - 备注
|
||||
* @param {number} [data.status] - 状态
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
export function createUser(data) {
|
||||
return request({
|
||||
url: '/admin/admin/edit',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户
|
||||
* @param {Object} data - 用户数据
|
||||
* @param {number} data.id - 用户ID
|
||||
* @param {string} [data.username] - 用户名
|
||||
* @param {string} [data.password] - 密码
|
||||
* @param {string} [data.nickname] - 昵称
|
||||
* @param {string} [data.phone] - 手机号
|
||||
* @param {string} [data.email] - 邮箱
|
||||
* @param {string} [data.head_img] - 头像
|
||||
* @param {string} [data.remark] - 备注
|
||||
* @param {number} [data.status] - 状态
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
export function updateUser(data) {
|
||||
return request({
|
||||
url: '/admin/admin/edit',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户
|
||||
* @param {number} id - 用户ID
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
export function deleteUser(id) {
|
||||
return request({
|
||||
url: '/admin/admin/delete',
|
||||
method: 'POST',
|
||||
data: { id },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除用户
|
||||
* @param {Array<number>} ids - 用户ID列表
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
export function batchDeleteUsers(ids) {
|
||||
return request({
|
||||
url: '/admin/admin/delete',
|
||||
method: 'POST',
|
||||
data: { ids },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改用户状态
|
||||
* @param {number} id - 用户ID
|
||||
* @param {number} status - 状态
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
export function updateUserStatus(id, status) {
|
||||
return request({
|
||||
url: '/admin/admin/edit',
|
||||
method: 'POST',
|
||||
data: { id, status },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置用户密码
|
||||
* @param {number} id - 用户ID
|
||||
* @param {string} password - 新密码
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
export function resetUserPassword(id, password) {
|
||||
return request({
|
||||
url: '/admin/admin/resetPassword',
|
||||
method: 'POST',
|
||||
data: { id, password },
|
||||
})
|
||||
}
|
||||
@@ -25,6 +25,36 @@
|
||||
"style": {
|
||||
"navigationBarTitleText": "登录"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/permission/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "权限管理"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "用户列表"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "用户详情"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/edit",
|
||||
"style": {
|
||||
"navigationBarTitleText": "编辑用户"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/delete",
|
||||
"style": {
|
||||
"navigationBarTitleText": "删除用户"
|
||||
}
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
|
||||
247
source/clients/uniapp/src/pages/permission/index.vue
Normal file
247
source/clients/uniapp/src/pages/permission/index.vue
Normal file
@@ -0,0 +1,247 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<view class="card">
|
||||
<up-form :model="form" labelPosition="top">
|
||||
<up-form-item label="权限名称">
|
||||
<up-input v-model="form.title" placeholder="请输入权限名称" />
|
||||
</up-form-item>
|
||||
<up-form-item label="权限标识">
|
||||
<up-input v-model="form.name" placeholder="请输入权限标识" />
|
||||
</up-form-item>
|
||||
<up-form-item label="权限类型">
|
||||
<up-picker :show="showTypePicker" :columns="typeOptions" @confirm="onTypeConfirm" @cancel="showTypePicker = false">
|
||||
<up-input v-model="form.typeLabel" placeholder="请选择权限类型" disabled />
|
||||
</up-picker>
|
||||
</up-form-item>
|
||||
</up-form>
|
||||
</view>
|
||||
|
||||
<view class="btn-row">
|
||||
<up-button type="primary" text="查询" @click="handleSearch" />
|
||||
<up-button type="default" text="重置" @click="handleReset" />
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="card-header">
|
||||
<text class="card-title">权限列表</text>
|
||||
<view class="card-actions">
|
||||
<up-button type="primary" size="mini" text="刷新" @click="fetchPermissions" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<up-empty v-if="!permissions.length && !loading" text="暂无权限数据" mode="data" />
|
||||
|
||||
<view v-for="item in permissions" :key="item.id" class="permission-item">
|
||||
<view class="item-main">
|
||||
<view class="item-title">{{ item.title }}</view>
|
||||
<view class="item-meta">
|
||||
<text class="tag">{{ item.type }}</text>
|
||||
<text class="status" :class="{ active: item.status === 1 }">
|
||||
{{ item.status === 1 ? '启用' : '禁用' }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="item-sub">
|
||||
<text class="item-name">{{ item.name }}</text>
|
||||
</view>
|
||||
<view class="item-actions">
|
||||
<up-button type="primary" size="mini" text="详情" @click="viewDetail(item.id)" />
|
||||
<up-button type="warning" size="mini" text="编辑" @click="editPermission(item.id)" />
|
||||
<up-button type="error" size="mini" text="删除" @click="deletePermission(item.id)" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<up-loading-page :loading="loading" loading-text="加载中..." />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getPermissionList, deletePermission as deletePermissionApi } from '../../api/permission'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
showTypePicker: false,
|
||||
form: {
|
||||
title: '',
|
||||
name: '',
|
||||
type: '',
|
||||
typeLabel: '',
|
||||
},
|
||||
typeOptions: [
|
||||
[
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '控制器', value: 'controller' },
|
||||
{ label: '方法', value: 'action' },
|
||||
],
|
||||
],
|
||||
permissions: [],
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
this.fetchPermissions()
|
||||
},
|
||||
methods: {
|
||||
onTypeConfirm(e) {
|
||||
const selected = e.value[0]
|
||||
this.form.type = selected.value
|
||||
this.form.typeLabel = selected.label
|
||||
this.showTypePicker = false
|
||||
},
|
||||
handleSearch() {
|
||||
this.fetchPermissions()
|
||||
},
|
||||
handleReset() {
|
||||
this.form = {
|
||||
title: '',
|
||||
name: '',
|
||||
type: '',
|
||||
typeLabel: '',
|
||||
}
|
||||
this.fetchPermissions()
|
||||
},
|
||||
async fetchPermissions() {
|
||||
this.loading = true
|
||||
try {
|
||||
const res = await getPermissionList({
|
||||
title: this.form.title,
|
||||
name: this.form.name,
|
||||
type: this.form.type,
|
||||
})
|
||||
this.permissions = res.data.list || res.data || []
|
||||
} catch (e) {
|
||||
uni.showToast({ title: '加载失败', icon: 'none' })
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
viewDetail(id) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/permission/detail?id=${id}`,
|
||||
})
|
||||
},
|
||||
editPermission(id) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/permission/edit?id=${id}`,
|
||||
})
|
||||
},
|
||||
async deletePermission(id) {
|
||||
uni.showModal({
|
||||
title: '确认删除',
|
||||
content: '确定要删除该权限吗?',
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
await deletePermissionApi(id)
|
||||
uni.showToast({ title: '删除成功', icon: 'none' })
|
||||
this.fetchPermissions()
|
||||
} catch (e) {
|
||||
uni.showToast({ title: '删除失败', icon: 'none' })
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.page {
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 20rpx;
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.card-actions {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.btn-row {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.permission-item {
|
||||
padding: 20rpx;
|
||||
border: 1rpx solid #f0f0f0;
|
||||
border-radius: 8rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.item-main {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.item-meta {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tag {
|
||||
padding: 4rpx 12rpx;
|
||||
background: #f0f0f0;
|
||||
border-radius: 4rpx;
|
||||
font-size: 22rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.status {
|
||||
padding: 4rpx 12rpx;
|
||||
background: #e0e0e0;
|
||||
border-radius: 4rpx;
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.status.active {
|
||||
background: #e6f7e6;
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.item-sub {
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.item-actions {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
}
|
||||
</style>
|
||||
233
source/clients/uniapp/src/pages/user/delete.vue
Normal file
233
source/clients/uniapp/src/pages/user/delete.vue
Normal file
@@ -0,0 +1,233 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<up-loading-page :loading="loading" loading-text="加载中..." />
|
||||
|
||||
<view v-if="user" class="page-content">
|
||||
<view class="card warning-card">
|
||||
<view class="warning-icon">⚠️</view>
|
||||
<view class="warning-title">确认删除用户</view>
|
||||
<view class="warning-desc">删除后用户数据将无法恢复,请谨慎操作</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="card-title">用户信息</view>
|
||||
<view class="info-list">
|
||||
<view class="info-item">
|
||||
<text class="label">用户名</text>
|
||||
<text class="value">{{ user.username }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">昵称</text>
|
||||
<text class="value">{{ user.nickname || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">手机号</text>
|
||||
<text class="value">{{ user.phone || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">状态</text>
|
||||
<text class="value status" :class="{ active: user.status === 1 }">
|
||||
{{ user.status === 1 ? '正常' : '禁用' }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="user.remark" class="card">
|
||||
<view class="card-title">备注信息</view>
|
||||
<view class="remark-content">{{ user.remark }}</view>
|
||||
</view>
|
||||
|
||||
<view class="confirm-section">
|
||||
<up-checkbox
|
||||
v-model="confirmed"
|
||||
shape="circle"
|
||||
label="我确认要删除该用户,且已知晓删除后无法恢复"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="btn-group">
|
||||
<up-button type="default" text="取消" @click="handleCancel" />
|
||||
<up-button
|
||||
type="error"
|
||||
:disabled="!confirmed || deleting"
|
||||
:loading="deleting"
|
||||
text="确认删除"
|
||||
@click="handleDelete"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getUserDetail, deleteUser as deleteUserApi } from '../../api/user'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
deleting: false,
|
||||
userId: null,
|
||||
user: null,
|
||||
confirmed: false,
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
if (options.id) {
|
||||
this.userId = parseInt(options.id)
|
||||
this.fetchUserDetail()
|
||||
} else {
|
||||
uni.showToast({ title: '参数错误', icon: 'none' })
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 1500)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async fetchUserDetail() {
|
||||
this.loading = true
|
||||
try {
|
||||
const res = await getUserDetail(this.userId)
|
||||
this.user = res.data.row || res.data || null
|
||||
} catch (e) {
|
||||
uni.showToast({ title: '加载失败', icon: 'none' })
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 1500)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
async handleDelete() {
|
||||
if (!this.confirmed) {
|
||||
uni.showToast({ title: '请先确认删除操作', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
this.deleting = true
|
||||
try {
|
||||
await deleteUserApi(this.userId)
|
||||
uni.showToast({ title: '删除成功', icon: 'none' })
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 1500)
|
||||
} catch (e) {
|
||||
uni.showToast({ title: '删除失败', icon: 'none' })
|
||||
} finally {
|
||||
this.deleting = false
|
||||
}
|
||||
},
|
||||
handleCancel() {
|
||||
uni.navigateBack()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
background: #f8f8f8;
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.page-content {
|
||||
padding-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 24rpx;
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.warning-card {
|
||||
background: linear-gradient(135deg, #fff7e6 0%, #fff1d6 100%);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.warning-icon {
|
||||
font-size: 80rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.warning-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #d46b08;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.warning-desc {
|
||||
font-size: 26rpx;
|
||||
color: #d48806;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #222;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.info-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12rpx 0;
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
}
|
||||
|
||||
.info-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 26rpx;
|
||||
color: #222;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.value.status {
|
||||
padding: 6rpx 16rpx;
|
||||
background: #e0e0e0;
|
||||
border-radius: 16rpx;
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.value.status.active {
|
||||
background: #e6f7e6;
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.remark-content {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.confirm-section {
|
||||
padding: 24rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
}
|
||||
</style>
|
||||
257
source/clients/uniapp/src/pages/user/detail.vue
Normal file
257
source/clients/uniapp/src/pages/user/detail.vue
Normal file
@@ -0,0 +1,257 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<up-loading-page :loading="loading" loading-text="加载中..." />
|
||||
|
||||
<view v-if="user" class="page-content">
|
||||
<view class="card">
|
||||
<view class="avatar-section">
|
||||
<image v-if="user.head_img" :src="user.head_img" class="avatar" mode="aspectFill" />
|
||||
<view v-else class="avatar-placeholder">{{ user.username?.charAt(0).toUpperCase() }}</view>
|
||||
<view class="user-info">
|
||||
<view class="username">{{ user.nickname || user.username }}</view>
|
||||
<view class="account">{{ user.username }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="card-title">基本信息</view>
|
||||
<view class="info-list">
|
||||
<view class="info-item">
|
||||
<text class="label">用户 ID</text>
|
||||
<text class="value">{{ user.id }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">用户名</text>
|
||||
<text class="value">{{ user.username }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">昵称</text>
|
||||
<text class="value">{{ user.nickname || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">手机号</text>
|
||||
<text class="value">{{ user.phone || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">邮箱</text>
|
||||
<text class="value">{{ user.email || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">状态</text>
|
||||
<text class="value status" :class="{ active: user.status === 1 }">
|
||||
{{ user.status === 1 ? '正常' : '禁用' }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="user.remark" class="card">
|
||||
<view class="card-title">备注信息</view>
|
||||
<view class="remark-content">{{ user.remark }}</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="card-title">时间信息</view>
|
||||
<view class="info-list">
|
||||
<view class="info-item">
|
||||
<text class="label">创建时间</text>
|
||||
<text class="value">{{ user.create_time || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">更新时间</text>
|
||||
<text class="value">{{ user.update_time || '-' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="btn-group">
|
||||
<up-button type="primary" text="编辑" @click="goEdit" />
|
||||
<up-button type="error" text="删除" @click="handleDelete" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getUserDetail, deleteUser as deleteUserApi } from '../../api/user'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
userId: null,
|
||||
user: null,
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
if (options.id) {
|
||||
this.userId = parseInt(options.id)
|
||||
this.fetchUserDetail()
|
||||
} else {
|
||||
uni.showToast({ title: '参数错误', icon: 'none' })
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 1500)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async fetchUserDetail() {
|
||||
this.loading = true
|
||||
try {
|
||||
const res = await getUserDetail(this.userId)
|
||||
this.user = res.data.row || res.data || null
|
||||
} catch (e) {
|
||||
uni.showToast({ title: '加载失败', icon: 'none' })
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
goEdit() {
|
||||
uni.navigateTo({
|
||||
url: `/pages/user/edit?id=${this.userId}`,
|
||||
})
|
||||
},
|
||||
handleDelete() {
|
||||
uni.showModal({
|
||||
title: '确认删除',
|
||||
content: '确定要删除该用户吗?',
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
await deleteUserApi(this.userId)
|
||||
uni.showToast({ title: '删除成功', icon: 'none' })
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 1500)
|
||||
} catch (e) {
|
||||
uni.showToast({ title: '删除失败', icon: 'none' })
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
background: #f8f8f8;
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.page-content {
|
||||
padding-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 24rpx;
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.avatar-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.avatar-placeholder {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: #fff;
|
||||
border-radius: 50%;
|
||||
font-size: 48rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
flex: 1;
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #222;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.account {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #222;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.info-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12rpx 0;
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
}
|
||||
|
||||
.info-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 26rpx;
|
||||
color: #222;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.value.status {
|
||||
padding: 6rpx 16rpx;
|
||||
background: #e0e0e0;
|
||||
border-radius: 16rpx;
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.value.status.active {
|
||||
background: #e6f7e6;
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.remark-content {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
}
|
||||
</style>
|
||||
263
source/clients/uniapp/src/pages/user/edit.vue
Normal file
263
source/clients/uniapp/src/pages/user/edit.vue
Normal file
@@ -0,0 +1,263 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<up-loading-page :loading="loading" loading-text="加载中..." />
|
||||
|
||||
<view class="card">
|
||||
<up-form :model="form" labelPosition="top">
|
||||
<up-form-item
|
||||
label="用户名"
|
||||
:required="!isEdit"
|
||||
:rules="[{ required: !isEdit, message: '请输入用户名', trigger: 'blur' }]"
|
||||
>
|
||||
<up-input
|
||||
v-model="form.username"
|
||||
placeholder="请输入用户名"
|
||||
:disabled="isEdit"
|
||||
/>
|
||||
</up-form-item>
|
||||
|
||||
<up-form-item
|
||||
v-if="!isEdit"
|
||||
label="密码"
|
||||
required
|
||||
:rules="[{ required: true, message: '请输入密码', trigger: 'blur' }]"
|
||||
>
|
||||
<up-input
|
||||
v-model="form.password"
|
||||
type="password"
|
||||
placeholder="请输入密码"
|
||||
/>
|
||||
</up-form-item>
|
||||
|
||||
<up-form-item
|
||||
v-if="!isEdit"
|
||||
label="确认密码"
|
||||
required
|
||||
:rules="[{ required: true, message: '请输入确认密码', trigger: 'blur' }]"
|
||||
>
|
||||
<up-input
|
||||
v-model="form.confirmPassword"
|
||||
type="password"
|
||||
placeholder="请输入确认密码"
|
||||
/>
|
||||
</up-form-item>
|
||||
|
||||
<up-form-item
|
||||
v-if="isEdit"
|
||||
label="新密码"
|
||||
>
|
||||
<up-input
|
||||
v-model="form.password"
|
||||
type="password"
|
||||
placeholder="留空则不修改密码"
|
||||
/>
|
||||
</up-form-item>
|
||||
|
||||
<up-form-item
|
||||
v-if="isEdit"
|
||||
label="确认新密码"
|
||||
>
|
||||
<up-input
|
||||
v-model="form.confirmPassword"
|
||||
type="password"
|
||||
placeholder="留空则不修改密码"
|
||||
/>
|
||||
</up-form-item>
|
||||
|
||||
<up-form-item label="昵称">
|
||||
<up-input
|
||||
v-model="form.nickname"
|
||||
placeholder="请输入昵称"
|
||||
/>
|
||||
</up-form-item>
|
||||
|
||||
<up-form-item label="手机号">
|
||||
<up-input
|
||||
v-model="form.phone"
|
||||
type="number"
|
||||
placeholder="请输入手机号"
|
||||
/>
|
||||
</up-form-item>
|
||||
|
||||
<up-form-item label="邮箱">
|
||||
<up-input
|
||||
v-model="form.email"
|
||||
placeholder="请输入邮箱"
|
||||
/>
|
||||
</up-form-item>
|
||||
|
||||
<up-form-item label="头像">
|
||||
<up-input
|
||||
v-model="form.head_img"
|
||||
placeholder="请输入头像 URL"
|
||||
/>
|
||||
</up-form-item>
|
||||
|
||||
<up-form-item label="状态">
|
||||
<up-radio-group v-model="form.status">
|
||||
<up-radio :name="1" label="正常" />
|
||||
<up-radio :name="0" label="禁用" />
|
||||
</up-radio-group>
|
||||
</up-form-item>
|
||||
|
||||
<up-form-item label="备注">
|
||||
<up-textarea
|
||||
v-model="form.remark"
|
||||
placeholder="请输入备注信息"
|
||||
:maxlength="200"
|
||||
:autoHeight="true"
|
||||
/>
|
||||
</up-form-item>
|
||||
</up-form>
|
||||
</view>
|
||||
|
||||
<view class="btn-group">
|
||||
<up-button type="default" text="取消" @click="handleCancel" />
|
||||
<up-button type="primary" :loading="submitting" text="保存" @click="handleSubmit" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getUserDetail, createUser, updateUser } from '../../api/user'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
submitting: false,
|
||||
userId: null,
|
||||
isEdit: false,
|
||||
form: {
|
||||
username: '',
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
nickname: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
head_img: '',
|
||||
status: 1,
|
||||
remark: '',
|
||||
},
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
if (options.id) {
|
||||
this.userId = parseInt(options.id)
|
||||
this.isEdit = true
|
||||
this.fetchUserDetail()
|
||||
} else {
|
||||
this.isEdit = false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async fetchUserDetail() {
|
||||
this.loading = true
|
||||
try {
|
||||
const res = await getUserDetail(this.userId)
|
||||
const user = res.data.row || res.data || {}
|
||||
this.form = {
|
||||
username: user.username || '',
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
nickname: user.nickname || '',
|
||||
phone: user.phone || '',
|
||||
email: user.email || '',
|
||||
head_img: user.head_img || '',
|
||||
status: user.status !== undefined ? user.status : 1,
|
||||
remark: user.remark || '',
|
||||
}
|
||||
} catch (e) {
|
||||
uni.showToast({ title: '加载失败', icon: 'none' })
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 1500)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
validateForm() {
|
||||
if (!this.form.username) {
|
||||
uni.showToast({ title: '请输入用户名', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
|
||||
if (!this.isEdit && !this.form.password) {
|
||||
uni.showToast({ title: '请输入密码', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
|
||||
if (this.form.password || this.form.confirmPassword) {
|
||||
if (this.form.password !== this.form.confirmPassword) {
|
||||
uni.showToast({ title: '两次密码不一致', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
async handleSubmit() {
|
||||
if (!this.validateForm()) {
|
||||
return
|
||||
}
|
||||
|
||||
this.submitting = true
|
||||
try {
|
||||
const data = {
|
||||
username: this.form.username,
|
||||
nickname: this.form.nickname,
|
||||
phone: this.form.phone,
|
||||
email: this.form.email,
|
||||
head_img: this.form.head_img,
|
||||
status: this.form.status,
|
||||
remark: this.form.remark,
|
||||
}
|
||||
|
||||
if (this.form.password) {
|
||||
data.password = this.form.password
|
||||
}
|
||||
|
||||
if (this.isEdit) {
|
||||
data.id = this.userId
|
||||
await updateUser(data)
|
||||
uni.showToast({ title: '更新成功', icon: 'none' })
|
||||
} else {
|
||||
await createUser(data)
|
||||
uni.showToast({ title: '创建成功', icon: 'none' })
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 1500)
|
||||
} catch (e) {
|
||||
uni.showToast({ title: '保存失败', icon: 'none' })
|
||||
} finally {
|
||||
this.submitting = false
|
||||
}
|
||||
},
|
||||
handleCancel() {
|
||||
uni.navigateBack()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
background: #f8f8f8;
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 24rpx;
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
}
|
||||
</style>
|
||||
363
source/clients/uniapp/src/pages/user/list.vue
Normal file
363
source/clients/uniapp/src/pages/user/list.vue
Normal file
@@ -0,0 +1,363 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<view class="card">
|
||||
<up-form :model="form" labelPosition="top">
|
||||
<up-form-item label="用户名">
|
||||
<up-input v-model="form.username" placeholder="请输入用户名" />
|
||||
</up-form-item>
|
||||
<up-form-item label="昵称">
|
||||
<up-input v-model="form.nickname" placeholder="请输入昵称" />
|
||||
</up-form-item>
|
||||
<up-form-item label="手机号">
|
||||
<up-input v-model="form.phone" placeholder="请输入手机号" />
|
||||
</up-form-item>
|
||||
<up-form-item label="状态">
|
||||
<up-picker :show="showStatusPicker" :columns="statusOptions" @confirm="onStatusConfirm" @cancel="showStatusPicker = false">
|
||||
<up-input v-model="form.statusLabel" placeholder="请选择状态" disabled />
|
||||
</up-picker>
|
||||
</up-form-item>
|
||||
</up-form>
|
||||
</view>
|
||||
|
||||
<view class="btn-row">
|
||||
<up-button type="primary" text="查询" @click="handleSearch" />
|
||||
<up-button type="default" text="重置" @click="handleReset" />
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="card-header">
|
||||
<text class="card-title">用户列表</text>
|
||||
<view class="card-actions">
|
||||
<up-button type="primary" size="mini" text="新增" @click="goCreate" />
|
||||
<up-button type="default" size="mini" text="刷新" @click="fetchUsers" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<up-empty v-if="!users.length && !loading" text="暂无用户数据" mode="data" />
|
||||
|
||||
<view v-for="item in users" :key="item.id" class="user-item">
|
||||
<view class="item-main">
|
||||
<view class="item-avatar">
|
||||
<image v-if="item.head_img" :src="item.head_img" class="avatar" mode="aspectFill" />
|
||||
<view v-else class="avatar-placeholder">{{ item.username?.charAt(0).toUpperCase() }}</view>
|
||||
</view>
|
||||
<view class="item-info">
|
||||
<view class="item-title">{{ item.nickname || item.username }}</view>
|
||||
<view class="item-username">{{ item.username }}</view>
|
||||
</view>
|
||||
<view class="item-status">
|
||||
<text class="status" :class="{ active: item.status === 1 }">
|
||||
{{ item.status === 1 ? '正常' : '禁用' }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="item-meta">
|
||||
<text v-if="item.phone" class="meta-item">📱 {{ item.phone }}</text>
|
||||
<text v-if="item.email" class="meta-item">📧 {{ item.email }}</text>
|
||||
</view>
|
||||
<view v-if="item.remark" class="item-remark">
|
||||
<text class="remark-label">备注:</text>
|
||||
<text class="remark-text">{{ item.remark }}</text>
|
||||
</view>
|
||||
<view class="item-actions">
|
||||
<up-button type="primary" size="mini" text="详情" @click="viewDetail(item.id)" />
|
||||
<up-button type="warning" size="mini" text="编辑" @click="editUser(item.id)" />
|
||||
<up-button type="error" size="mini" text="删除" @click="deleteUser(item.id)" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="pagination.total > 0" class="pagination">
|
||||
<up-button
|
||||
:disabled="pagination.page <= 1"
|
||||
size="mini"
|
||||
text="上一页"
|
||||
@click="prevPage"
|
||||
/>
|
||||
<text class="page-info">{{ pagination.page }} / {{ Math.ceil(pagination.total / pagination.limit) }}</text>
|
||||
<up-button
|
||||
:disabled="pagination.page >= Math.ceil(pagination.total / pagination.limit)"
|
||||
size="mini"
|
||||
text="下一页"
|
||||
@click="nextPage"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<up-loading-page :loading="loading" loading-text="加载中..." />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getUserList, deleteUser as deleteUserApi } from '../../api/user'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
showStatusPicker: false,
|
||||
form: {
|
||||
username: '',
|
||||
nickname: '',
|
||||
phone: '',
|
||||
status: '',
|
||||
statusLabel: '',
|
||||
},
|
||||
statusOptions: [
|
||||
[
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '正常', value: 1 },
|
||||
{ label: '禁用', value: 0 },
|
||||
],
|
||||
],
|
||||
users: [],
|
||||
pagination: {
|
||||
page: 1,
|
||||
limit: 10,
|
||||
total: 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
this.fetchUsers()
|
||||
},
|
||||
methods: {
|
||||
onStatusConfirm(e) {
|
||||
const selected = e.value[0]
|
||||
this.form.status = selected.value
|
||||
this.form.statusLabel = selected.label
|
||||
this.showStatusPicker = false
|
||||
},
|
||||
handleSearch() {
|
||||
this.pagination.page = 1
|
||||
this.fetchUsers()
|
||||
},
|
||||
handleReset() {
|
||||
this.form = {
|
||||
username: '',
|
||||
nickname: '',
|
||||
phone: '',
|
||||
status: '',
|
||||
statusLabel: '',
|
||||
}
|
||||
this.pagination.page = 1
|
||||
this.fetchUsers()
|
||||
},
|
||||
async fetchUsers() {
|
||||
this.loading = true
|
||||
try {
|
||||
const res = await getUserList({
|
||||
page: this.pagination.page,
|
||||
limit: this.pagination.limit,
|
||||
username: this.form.username,
|
||||
nickname: this.form.nickname,
|
||||
phone: this.form.phone,
|
||||
status: this.form.status,
|
||||
})
|
||||
this.users = res.data.list || res.data || []
|
||||
this.pagination.total = res.data.total || 0
|
||||
} catch (e) {
|
||||
uni.showToast({ title: '加载失败', icon: 'none' })
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
prevPage() {
|
||||
if (this.pagination.page > 1) {
|
||||
this.pagination.page--
|
||||
this.fetchUsers()
|
||||
}
|
||||
},
|
||||
nextPage() {
|
||||
const maxPage = Math.ceil(this.pagination.total / this.pagination.limit)
|
||||
if (this.pagination.page < maxPage) {
|
||||
this.pagination.page++
|
||||
this.fetchUsers()
|
||||
}
|
||||
},
|
||||
goCreate() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/user/edit',
|
||||
})
|
||||
},
|
||||
viewDetail(id) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/user/detail?id=${id}`,
|
||||
})
|
||||
},
|
||||
editUser(id) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/user/edit?id=${id}`,
|
||||
})
|
||||
},
|
||||
async deleteUser(id) {
|
||||
uni.showModal({
|
||||
title: '确认删除',
|
||||
content: '确定要删除该用户吗?',
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
await deleteUserApi(id)
|
||||
uni.showToast({ title: '删除成功', icon: 'none' })
|
||||
this.fetchUsers()
|
||||
} catch (e) {
|
||||
uni.showToast({ title: '删除失败', icon: 'none' })
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.page {
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 20rpx;
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.card-actions {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.btn-row {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.user-item {
|
||||
padding: 20rpx;
|
||||
border: 1rpx solid #f0f0f0;
|
||||
border-radius: 8rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.item-main {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.item-avatar {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.avatar-placeholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: #fff;
|
||||
border-radius: 50%;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.item-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #222;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
|
||||
.item-username {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.item-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.status {
|
||||
padding: 6rpx 16rpx;
|
||||
background: #e0e0e0;
|
||||
border-radius: 16rpx;
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.status.active {
|
||||
background: #e6f7e6;
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.item-meta {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.meta-item {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.item-remark {
|
||||
margin-bottom: 12rpx;
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.remark-label {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.remark-text {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.item-actions {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 20rpx;
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
|
||||
.page-info {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
||||
131
source/clients/uniapp/src/types/index.ts
Normal file
131
source/clients/uniapp/src/types/index.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* API 响应基础类型
|
||||
*/
|
||||
export interface ApiResponse<T = any> {
|
||||
code: number
|
||||
msg: string
|
||||
data: T
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页响应类型
|
||||
*/
|
||||
export interface PageResponse<T = any> {
|
||||
total: number
|
||||
page: number
|
||||
limit: number
|
||||
list: T[]
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页请求参数类型
|
||||
*/
|
||||
export interface PageParams {
|
||||
page?: number
|
||||
limit?: number
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户信息类型
|
||||
*/
|
||||
export interface User {
|
||||
id: number
|
||||
username: string
|
||||
nickname?: string
|
||||
phone?: string
|
||||
email?: string
|
||||
head_img?: string
|
||||
remark?: string
|
||||
status?: number
|
||||
create_time?: string
|
||||
update_time?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录请求参数类型
|
||||
*/
|
||||
export interface LoginParams {
|
||||
username: string
|
||||
password: string
|
||||
captcha?: string
|
||||
keep_login?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录响应类型
|
||||
*/
|
||||
export interface LoginResponse {
|
||||
token: string
|
||||
admin?: User
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限节点类型
|
||||
*/
|
||||
export interface Permission {
|
||||
id: number
|
||||
pid: number
|
||||
name: string
|
||||
title: string
|
||||
type: string
|
||||
status: number
|
||||
sort: number
|
||||
create_time?: string
|
||||
update_time?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 菜单树节点类型
|
||||
*/
|
||||
export interface MenuNode extends Permission {
|
||||
children?: MenuNode[]
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户列表查询参数类型
|
||||
*/
|
||||
export interface UserListParams extends PageParams {
|
||||
username?: string
|
||||
nickname?: string
|
||||
phone?: string
|
||||
status?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户表单数据类型
|
||||
*/
|
||||
export interface UserFormData {
|
||||
id?: number
|
||||
username: string
|
||||
password?: string
|
||||
nickname?: string
|
||||
phone?: string
|
||||
email?: string
|
||||
head_img?: string
|
||||
remark?: string
|
||||
status?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限列表查询参数类型
|
||||
*/
|
||||
export interface PermissionListParams extends PageParams {
|
||||
name?: string
|
||||
title?: string
|
||||
type?: string
|
||||
status?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限表单数据类型
|
||||
*/
|
||||
export interface PermissionFormData {
|
||||
id?: number
|
||||
pid: number
|
||||
name: string
|
||||
title: string
|
||||
type: string
|
||||
status?: number
|
||||
sort?: number
|
||||
}
|
||||
25
source/clients/uniapp/tsconfig.json
Normal file
25
source/clients/uniapp/tsconfig.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "ESNext",
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "preserve",
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"types": ["@dcloudio/types"]
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
|
||||
"exclude": ["dist", "node_modules"]
|
||||
}
|
||||
11
source/clients/uniapp/tsconfig.node.json
Normal file
11
source/clients/uniapp/tsconfig.node.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true
|
||||
},
|
||||
"include": ["vite.config.js"]
|
||||
}
|
||||
Reference in New Issue
Block a user