Files
akmon/pages/admins/user-management.uvue
2026-01-20 08:04:15 +08:00

304 lines
7.6 KiB
Plaintext
Raw Blame History

<template>
<view class="user-management-page">
<text class="page-title">{{ $t('user_mgmt.title') }}</text>
<view class="search-bar">
<input class="searchinput" type="text" v-model="searchQuery" placeholder="Search users..." />
<button class="searchbutton" @click="searchUsers">{{ $t('common.search') }}</button>
</view>
<supadb
ref="userdb"
:collection="'ak_users'"
:field="'*'"
:filter="where"
:orderby="'id.desc'"
:page-size="pageSize"
:page-current="pageCurrent"
getcount="exact"
loadtime="manual"
v-slot:default="{ data, pagination, loading, current, total, error }"
@load="onUserLoad"
>
<view v-if="loading">{{ $t('common.loading') }}</view>
<view v-else-if="error">{{ error }}</view>
<view v-else>
<!-- Large Screen Layout (Table) -->
<view class="table-view">
<view class="table-header">
<text>{{ $t('user.username') }}</text>
<text>{{ $t('user.email') }}</text>
<text>{{ $t('user.role') }}</text>
<text>{{ $t('user.created_at') }}</text>
<text>{{ $t('common.action') }}</text>
</view>
<view v-for="(user, idx) in (data as Array<UTSJSONObject>)" :key="user.get('id')" :class="['table-row', idx % 2 === 0 ? 'row-even' : 'row-odd']">
<text>{{ user.get('username') }}</text>
<text>{{ user.get('email') }}</text>
<text>{{ user.get('role') }}</text>
<text>{{ user.get('created_at') }}</text>
<view class="action-buttons">
<button @click="editUser(user)">{{ $t('common.edit') }}</button>
<button @click="deleteUser(user)">{{ $t('common.delete') }}</button>
</view>
</view>
</view>
<!-- Small Screen Layout (Cards) -->
<view class="card-view">
<view v-for="(user, idx) in (data as Array<UTSJSONObject>)" :key="user.get('id')" :class="['user-card', idx % 2 === 0 ? 'row-even' : 'row-odd']">
<view class="card-row">
<text class="card-label">{{ $t('user.username') }}:</text>
<text class="card-value">{{ user.get('username') }}</text>
</view>
<view class="card-row">
<text class="card-label">{{ $t('user.email') }}:</text>
<text class="card-value">{{ user.get('email') }}</text>
</view>
<view class="card-row">
<text class="card-label">{{ $t('user.role') }}:</text>
<text class="card-value">{{ user.get('role') }}</text>
</view>
<view class="card-row">
<text class="card-label">{{ $t('user.created_at') }}:</text>
<text class="card-value">{{ user.get('created_at') }}</text>
</view>
<view class="card-actions">
<button @click="editUser(user)">{{ $t('common.edit') }}</button>
<button @click="deleteUser(user)">{{ $t('common.delete') }}</button>
</view>
</view>
</view>
<view class="pagination">
<button @click="prevPage(pagination!!)">{{ $t('common.prev') }}</button>
<text>{{ current}}/{{ total }}</text>
<button @click="nextPage(pagination!!)">{{ $t('common.next') }}</button>
<button v-if="hasMoreData" @click="continueIteration()">{{ $t('common.continueToIterate') }}</button>
</view>
</view>
</supadb>
<!-- 编辑/新增弹窗等可后续补充 -->
</view>
</template>
<script lang="uts">
import supa from '@/components/supadb/aksupainstance.uts';
export default {
name: 'UserManagement',
data() {
return {
supadb: null as SupadbComponentPublicInstance | null,
where: {},
pageSize: 10,
pageCurrent: 1,
searchQuery: '',
screenWidth: uni.getSystemInfoSync().windowWidth,
hasMoreData: false
}
},
onReady() {
this.supadb = this.$refs["userdb"] as SupadbComponentPublicInstance;
// supa.from('system_dept').select(*).or('"name.like.%20%,email.like.%202%");
this.supadb?.loadData?.({ clear: false })
},
methods: {
onUserLoad(data: any[]) {
// 可处理数<E79086>?
this.hasMoreData = data.length === this.pageSize;
},
async prevPage(pagination: any) {
this.pageCurrent--;
await nextTick();
this.supadb?.loadData?.({ clear: false })
},
async nextPage(pagination: any) {
this.pageCurrent++;
await nextTick();
this.supadb?.loadData?.({ clear: false })
},
editUser(user: UTSJSONObject) {
// 编辑逻辑
console.log(user)
uni.navigateTo({
url:'/pages/admins/users/detail?id='+ user.getString("id")
})
},
deleteUser(user: any) {
// 删除逻辑
},
async searchUsers() {
// Implement search logic
if (this.searchQuery!=null) {
// this.where = {
// text: this.searchQuery,
// column: ['username', 'email', 'role']
// };
// this.where = { email: { ilike: '%'+this.searchQuery+'%' }} as UTSJSONObject
this.where ={ or:"username.like.%"+this.searchQuery+"%,email.like.%"+this.searchQuery+"%"}
} else {
this.where = {};
}
console.log(this.where)
this.pageCurrent = 1;
await nextTick();
this.supadb?.loadData?.({ clear: true });
},
async continueIteration() {
this.pageCurrent++;
await nextTick();
this.supadb?.loadData?.({ clear: false })
}
},
onResize(size) {
// 这里处理页面尺寸变化
this.screenWidth = size.size.windowWidth;
}
}
</script>
<style scoped>
.user-management-page {
padding: 20px;
}
.search-bar {
display: flex;
margin-bottom: 16px;
padding: 8px;
}
/* Table View (Large Screens) */
.table-view {
display: flex;
flex-direction: column;
width: 100%;
}
.table-header, .table-row {
display: flex;
width: 100%;
padding: 16px;
align-items: center;
flex-direction: row;
}
.table-header {
font-weight: bold;
padding-bottom: 8px;
border-bottom: 1px solid #ccc;
}
.table-header text, .table-row text {
flex: 1;
padding: 8px 4px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.table-row {
border-bottom: 1px solid #eee;
padding: 8px 0;
}
.table-row:nth-child(even) {
background-color: #f7f7fa;
}
.table-row:nth-child(odd) {
background-color: #fff;
}
.action-buttons {
display: flex;
padding: 8px;
}
/* Card View (Small Screens) */
.card-view {
display: none;
flex-direction: column;
width: 100%;
padding: 16px;
}
.user-card {
border: 1px solid #eee;
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.user-card:nth-child(even) {
background-color: #f7f7fa;
}
.user-card:nth-child(odd) {
background-color: #fff;
}
.card-row {
display: flex;
padding: 4px 0;
}
.card-label {
font-weight: bold;
width: 100px;
}
.card-value {
flex: 1;
}
.card-actions {
display: flex;
padding: 8px;
margin-top: 12px;
/* justify-content: flex-end; */
}
.pagination {
margin-top: 16px;
display: flex;
align-items: center;
padding: 16px;
justify-content: center;
}
/* Responsive Design */
@media screen and (max-width: 768px) {
.table-view {
display: none;
}
.card-view {
display: flex;
}
.search-bar {
flex-direction: row;
justify-content: flex-end;
}
}
@media screen and (min-width: 769px) {
.table-view {
display: flex;
}
.card-view {
display: none;
}
}
.row-even { background-color: #f7f7fa; }
.row-odd { background-color: #fff; }
.searchbutton{
width:200rpx;
height:80rpx;
}
.searchinput{
flex:1;
}
</style>