2026.1.29

This commit is contained in:
ryx 2026-01-29 21:27:08 +08:00
parent 662d1193de
commit aad8d2e96e
10 changed files with 1077 additions and 537 deletions

View File

@ -412,13 +412,14 @@
]
},
{
"root": "pages/lab",
"pages": [
{
"path": "userlist"
}
"root": "pages/lab",
"pages": [
{
"path": "userlist"
},
{
"path":"useradd"
}
]
}
]

View File

@ -0,0 +1,305 @@
<template>
<view class="ciy-page">
<!-- 页面标题 -->
<view class="ciy-title mk bg2">
<view class="title">新增实验室成员</view>
<button class="btn def sm" @click="goBack">返回</button>
</view>
<!-- 表单容器 -->
<view class="main bg4 flex1 overflow-auto px4 py3">
<form class="ciy-form-group">
<!-- 1. 姓名必填 -->
<view class="ciy-form">
<label class="imp char4">姓名</label>
<input
type="text"
v-model="form.name"
placeholder="请输入成员姓名"
class="ciy-edit px2 py1 bg1 rounded"
required
/>
</view>
<!-- 2. 头像上传 -->
<view class="ciy-form">
<label class="char4">头像</label>
<view class="flex flex-center">
<image
:src="form.icon || '/lab/default-avatar.png'"
mode="aspectFill"
class="w-12 h-12 rounded-full mr3"
></image>
<button class="btn def sm" @click="chooseAvatar">选择图片</button>
</view>
</view>
<!-- 3. 头衔 -->
<view class="ciy-form">
<label class="char4">头衔</label>
<picker
mode="selector"
:range="titleOptions"
:range-key="'label'"
v-model="form.usertitle"
class="px2 py1 bg1 rounded w-full"
>
<view class="txt-left px2 py1">{{ getTitleText(form.usertitle) }}</view>
</picker>
</view>
<!-- 4. 成员状态必填 -->
<view class="ciy-form">
<label class="imp char4">状态</label>
<picker
mode="selector"
:range="statusOptions"
:range-key="'label'"
v-model="form.role"
class="px2 py1 bg1 rounded w-full"
required
>
<view class="txt-left px2 py1">{{ statusMap[form.role] }}</view>
</picker>
</view>
<!-- 5. 学历 -->
<view class="ciy-form">
<label class="char4">学历</label>
<picker
mode="selector"
:range="educationOptions"
:range-key="'label'"
v-model="form.education"
class="px2 py1 bg1 rounded w-full"
>
<view class="txt-left px2 py1">{{ educationMap[form.education] }}</view>
</picker>
</view>
<!-- 6. 编号 -->
<view class="ciy-form">
<label class="char4">编号</label>
<input
type="text"
v-model="form.sn"
placeholder="如 LAB-2024-001"
class="ciy-edit px2 py1 bg1 rounded"
/>
</view>
<!-- 7. 性别 -->
<view class="ciy-form">
<label class="char4">性别</label>
<picker
mode="selector"
:range="sexOptions"
:range-key="'label'"
v-model="form.sex"
class="px2 py1 bg1 rounded w-full"
>
<view class="txt-left px2 py1">{{ sexMap[form.sex] }}</view>
</picker>
</view>
<!-- 8. 加入日期 -->
<view class="ciy-form">
<label class="char4">加入日期</label>
<picker
mode="date"
v-model="form.addtimesText"
start="2000-01-01"
end="2100-12-31"
class="px2 py1 bg1 rounded w-full"
>
<view class="txt-left px2 py1">{{ form.addtimesText || '请选择日期' }}</view>
</picker>
</view>
<!-- 9. 手机号 -->
<view class="ciy-form">
<label class="char4">手机号</label>
<input
type="number"
v-model="form.mobile"
placeholder="请输入11位手机号"
class="ciy-edit px2 py1 bg1 rounded"
maxlength="11"
/>
</view>
<!-- 10. 邮箱 -->
<view class="ciy-form">
<label class="char4">邮箱</label>
<input
type="email"
v-model="form.email"
placeholder="请输入邮箱地址"
class="ciy-edit px2 py1 bg1 rounded"
/>
</view>
<!-- 11. 密码默认初始密码 -->
<view class="ciy-form">
<label class="char4">初始密码</label>
<input
type="password"
v-model="form.password"
placeholder="默认123456"
class="ciy-edit px2 py1 bg1 rounded"
:value="form.password || '123456'"
/>
</view>
</form>
<!-- 表单提交按钮 -->
<view class="ciy-form-bottom mt4">
<button
class="btn man lgg long"
@click="submitForm"
:disabled="isSubmitting"
>
{{ isSubmitting ? '提交中...' : '提交成员信息' }}
</button>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
isSubmitting: false, //
form: {
name: '', //
icon: '', //
usertitle: 0, //
role: 30, //
education: 50, //
sn: '', //
sex: 90, //
addtimesText: '', //
addtimes: 0, //
mobile: '', //
email: '', //
password: '123456' //
},
// lab_user.sql
titleOptions: [
{ value: 0, label: '无头衔' },
{ value: 10, label: '主任' },
{ value: 20, label: '副主任' },
{ value: 30, label: '顾问' },
{ value: 40, label: '名誉主任' },
{ value: 50, label: '教授' },
{ value: 60, label: '副教授' },
{ value: 70, label: '讲师' },
{ value: 80, label: '研究员' }
],
statusOptions: [
{ value: 10, label: '负责人' },
{ value: 20, label: '科研秘书' },
{ value: 30, label: '在册成员' },
{ value: 40, label: '历史成员' },
{ value: 50, label: '外部成员' }
],
educationOptions: [
{ value: 50, label: '本科' },
{ value: 60, label: '硕士' },
{ value: 70, label: '博士' }
],
sexOptions: [
{ value: 10, label: '男' },
{ value: 20, label: '女' },
{ value: 90, label: '其他' }
],
//
statusMap: { 10: '负责人', 20: '科研秘书', 30: '在册成员', 40: '历史成员', 50: '外部成员' },
educationMap: { 50: '本科', 60: '硕士', 70: '博士' },
sexMap: { 10: '男', 20: '女', 90: '其他' },
titleMap: { 0: '无头衔', 10: '主任', 20: '副主任', 30: '顾问', 40: '名誉主任', 50: '教授', 60: '副教授', 70: '讲师', 80: '研究员' }
};
},
watch: {
//
'form.addtimesText'(val) {
if (val) {
this.form.addtimes = new Date(val).getTime();
}
}
},
methods: {
//
goBack() {
uni.navigateBack({ delta: 1 });
},
// /
chooseAvatar() {
uni.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
const tempFilePath = res.tempFilePaths[0];
//
this.form.icon = tempFilePath;
}
});
},
//
getTitleText(usertitle) {
return this.titleMap[usertitle] || '无头衔';
},
//
async submitForm() {
// 1.
if (!this.form.name) {
return uni.showToast({ title: '请输入成员姓名', icon: 'none' });
}
if (!this.form.addtimes) {
return uni.showToast({ title: '请选择加入日期', icon: 'none' });
}
// 2.
this.isSubmitting = true;
try {
// 3.
console.log('提交的成员信息:', this.form);
// 4.
uni.showToast({ title: '新增成员成功', icon: 'success' });
// 5.
setTimeout(() => {
this.goBack();
}, 1500);
} catch (err) {
uni.showToast({ title: '提交失败,请重试', icon: 'none' });
console.error('新增成员失败:', err);
} finally {
// 6.
this.isSubmitting = false;
}
}
}
};
</script>
<style scoped>
/* 补充样式:适配表单布局 */
.ciy-edit {
width: 100%;
height: 2rem;
border: 1px solid var(--bg6);
border-radius: 0.3rem;
}
.w-12 { width: 3rem; }
.h-12 { height: 3rem; }
</style>

View File

@ -1,105 +1,114 @@
<template>
<view class="container">
<!-- 搜索栏按姓名/头衔搜索 -->
<view class="search my2">
<div class="flex flex-center">
<view class="ciy-page container">
<!-- 页面头部搜索栏 + 新增按钮 -->
<view class="main-header bg-white px4 py3 flex flex-center justify-between shadow-sm">
<!-- 搜索框区域 -->
<view class="search-container flex flex1 mr3 relative">
<icon class="search-icon" type="search" size="14" color="#999" />
<input
type="text"
v-model="searchKey"
placeholder="请输入姓名/头衔搜索"
class="flex1 px2 py1"
@confirm="getList"
class="flex1 px4 py2 pl-12 bg-gray-50 border border-gray-200 rounded-lg focus-border-primary focus-ring-1"
@confirm="getMemberList"
/>
<button class="btn man ml2" @click="getList">搜索</button>
</div>
<button class="btn btn-primary ml2" @click="getMemberList">搜索</button>
</view>
<!-- 新增成员按钮缩小尺寸 -->
<button class="btn btn-success btn-sm" @click="goToAddMember">新增成员</button>
</view>
<!-- 新增按钮 -->
<button class="btn succ my2" @click="goToEdit">新增成员</button>
<!-- 成员列表 -->
<div class="table bg1 rounded">
<!-- 表头 -->
<div class="flex bg5 txt6 py2 px2">
<div class="col-3 txt-center">头像</div>
<div class="col-4 txt-center">姓名</div>
<div class="col-4 txt-center">手机号</div>
<div class="col-4 txt-center">头衔</div>
<div class="col-4 txt-center">状态</div>
<div class="col-3 txt-center">性别</div>
<div class="col-3 txt-center">学历</div>
<div class="col-3 txt-center">操作</div>
</div>
<!-- 列表内容 -->
<div class="list" v-if="list.length > 0">
<div
class="flex py2 px2 border-b border-bg6"
v-for="item in list"
:key="item.id"
>
<div class="col-3 txt-center">
<image :src="item.avatar" mode="aspectFill" class="w-10 h-10 rounded-full"></image>
</div>
<div class="col-4 txt-center">{{ item.name }}</div>
<div class="col-4 txt-center">{{ item.mobile }}</div>
<div class="col-4 txt-center">{{ getTitleText(item.usertitle) }}</div>
<div class="col-4 txt-center">
<span :class="getStatusTagClass(item.stpstatus)">{{ getStatusText(item.stpstatus) }}</span>
</div>
<div class="col-3 txt-center">{{ getSexText(item.sex) }}</div>
<div class="col-3 txt-center">{{ getEducationText(item.education) }}</div>
<div class="col-3 txt-center flex flex-center justify-center gap2">
<button class="btn def sm" @click.stop="goToEdit(item.id)">编辑</button>
<button class="btn dag sm" @click.stop="delMember(item.id)">删除</button>
<button
class="btn sm"
:class="item.stpstatus === 30 ? 'warn' : 'succ'"
@click.stop="auditMember(item.id, item.stpstatus === 30 ? 40 : 30)"
>
{{ item.stpstatus === 30 ? '设为历史' : '设为在册' }}
</button>
</div>
</div>
</div>
<!-- 空数据提示 -->
<div class="txt-center py10 txt3" v-if="list.length === 0">
暂无成员数据
</div>
</div>
<!-- 分页 -->
<div class="table page flex justify-end mt2" v-if="total > pageSize">
<!-- 状态筛选栏按成员状态过滤缩小按钮尺寸 -->
<view class="status-filter bg-white px4 py3 flex overflow-x-auto scrollbar-hide border-b border-gray-100">
<button
class="btn def sm mr1"
@click="page--; getList()"
:disabled="page <= 1"
>上一页</button>
<span class="px2">{{ page }} / {{ Math.ceil(total / pageSize) }}</span>
<button
class="btn def sm ml1"
@click="page++; getList()"
:disabled="page >= Math.ceil(total / pageSize)"
>下一页</button>
</div>
v-for="(item, key) in statusMap"
:key="key"
class="status-btn mr2 flex-none rounded-full py1 px3 transition-all duration-300"
:class="activeStatus === key ? 'status-btn-active' : 'status-btn-default'"
@click="handleStatusChange(key)"
>
{{ item }}
</button>
</view>
<!-- 成员列表区域卡片式展示每页6条缩小卡片及内部元素 -->
<view class="main bg-gray-50 flex1 overflow-auto px4 py4">
<!-- 无数据提示缩小尺寸 -->
<view v-if="memberList.length === 0" class="empty-tip txt-center py12 bg-white rounded-xl shadow-sm mx-auto w-4-5">
<view class="empty-icon bg-gray-100 w-12 h-12 rounded-full flex flex-center justify-center mx-auto mb3">
<text class="text-gray-400 text-xl">👤</text>
</view>
<view class="text-gray-400 text-base">暂无当前状态的成员数据</view>
</view>
<!-- 成员卡片网格3列布局缩小间距 -->
<view class="ciy-grid grid3 gap-3" v-else>
<view class="grid-item" v-for="(member, index) in memberList" :key="member.id">
<!-- 成员卡片缩小整体尺寸 -->
<view class="member-card bg-white rounded-xl shadow-md transition-all duration-300 overflow-hidden">
<!-- 文字头像缩小尺寸 -->
<view class="card-avatar txt-center py3">
<view
class="avatar-placeholder w-16 h-16 rounded-full border-3 border-white shadow-sm mx-auto flex flex-center justify-center"
:style="{ backgroundColor: getAvatarBgColor(member.role) }"
>
<text class="text-lg font-bold text-white">{{ member.name.substring(0, 1) }}</text>
</view>
</view>
<!-- 成员信息缩小文字及内边距 -->
<view class="card-info txt-center px3 py4">
<view class="member-name text-lg font-bold text-gray-800 mb1">{{ member.name }}</view>
<view class="member-title text-sm text-gray-500 mb2">{{ getTitleText(member.usertitle) }}</view>
<!-- 状态标签缩小尺寸 -->
<view class="member-status">
<span
class="px2 py1 rounded-full text-xs font-medium"
:style="{
backgroundColor: getTagBgColor(member.role),
color: '#FFFFFF'
}"
>
{{ statusMap[member.role] }}
</span>
</view>
</view>
</view>
</view>
</view>
<!-- 滚动加载提示缩小尺寸 -->
<view v-if="hasMoreData" class="load-more txt-center py4 text-gray-400">
<view class="flex flex-center justify-center">
<loading class="mr2" size="12"></loading>
<text class="text-sm">下拉加载更多...</text>
</view>
</view>
</view>
</view>
</template>
<script>
import { request } from '@/util/request';
export default {
data() {
return {
list: [], //
searchKey: '', // /
searchKey: '', //
activeStatus: '', //
memberList: [], //
page: 1, //
pageSize: 15, //
total: 0, //
sexMap: {
10: '男',
20: '女',
90: '其他'
pageSize: 6, //
hasMoreData: true, //
//
statusMap: {
10: '负责人',
20: '科研秘书',
30: '在册成员',
40: '历史成员',
50: '外部成员'
},
//
titleMap: {
10: '主任',
20: '副主任',
@ -108,171 +117,381 @@ export default {
50: '教授',
60: '副教授',
70: '讲师',
80: '研究员'
80: '研究员',
0: '无头衔'
},
statusMap: {
10: '负责人',
20: '科研秘书',
30: '在册成员',
40: '历史成员',
50: '外部成员'
},
educationMap: {
50: '本科',
60: '硕士',
70: '博士'
//
roleColors: {
10: '#2563EB', // -
20: '#9333EA', // -
30: '#10B981', // -绿
40: '#9CA3AF', // -
50: '#F97316' // -
}
};
},
onLoad() {
this.getList();
//
this.activeStatus = '';
this.getMemberList();
//
const main = uni.createSelectorQuery().in(this).select('.main');
main.onScroll((res) => {
const { scrollTop, scrollHeight, clientHeight } = res;
if (scrollTop + clientHeight >= scrollHeight - 100 && this.hasMoreData) {
this.page++;
this.getMemberList(true);
}
}).exec();
},
methods: {
// /
async getList() {
try {
const res = await uni.request({
url: '/web/ambap/member.php',
method: 'POST',
data: {
act: 'member.list',
page: this.page,
pageSize: this.pageSize,
searchKey: this.searchKey, // /
searchType: 'name,title' // +
}
});
//
if (res.data.code === 1) {
this.list = res.data.list;
this.total = res.data.total;
} else {
uni.showToast({
title: res.data.errmsg || '获取列表失败',
icon: 'none'
});
}
} catch (err) {
uni.showToast({
title: '网络错误',
icon: 'none'
});
console.error('获取列表失败:', err);
}
async getMemberList(isAppend = false) {
try {
//
const params = {
page: this.page,
pagesize: this.pageSize,
name: this.searchKey, // name
role: this.activeStatus || 0, // role
status: 0, // stpstatus
action: 'get_list' // member.php
};
//
const res = await request('/member.php', params);
//
const { list, total } = res;
if (isAppend) {
this.memberList = [...this.memberList, ...list];
} else {
this.memberList = list;
}
//
this.hasMoreData = this.memberList.length < total;
} catch (err) {
console.error('获取成员列表失败:', err);
this.hasMoreData = false; //
}
},
//
handleStatusChange(status) {
this.activeStatus = status;
this.page = 1;
this.getMemberList();
},
//
goToEdit(id) {
uni.navigateTo({
url: `/pages/lab/useredit?id=${id || ''}`
});
//
goToAddMember() {
uni.navigateTo({ url: '/pages/lab/useradd' });
},
//
async delMember(id) {
const confirm = await uni.showModal({
title: '提示',
content: '确定要删除该成员吗?'
});
if (!confirm.confirm) return;
try {
const res = await uni.request({
url: '/web/ambap/member.php',
method: 'POST',
data: {
act: 'member.del',
id: id
}
});
if (res.data.code === 1) {
uni.showToast({
title: '删除成功',
icon: 'success'
});
this.getList();
} else {
uni.showToast({
title: res.data.errmsg || '删除失败',
icon: 'none'
});
}
} catch (err) {
uni.showToast({
title: '网络错误',
icon: 'none'
});
console.error('删除失败:', err);
}
//
getTitleText(usertitle) {
return this.titleMap[usertitle] || '未知头衔';
},
//
async auditMember(id, status) {
try {
const res = await uni.request({
url: '/web/ambap/member.php',
method: 'POST',
data: {
act: 'member.audit',
id: id,
status: status
}
});
if (res.data.code === 1) {
uni.showToast({
title: '操作成功',
icon: 'success'
});
this.getList();
} else {
uni.showToast({
title: res.data.errmsg || '操作失败',
icon: 'none'
});
}
} catch (err) {
uni.showToast({
title: '网络错误',
icon: 'none'
});
console.error('状态切换失败:', err);
}
//
getTagBgColor(role) {
return this.roleColors[role] || '#E5E7EB';
},
//
getSexText(sex) {
return this.sexMap[sex] || '未知';
},
//
getTitleText(title) {
return this.titleMap[title] || '未知头衔';
},
//
getStatusText(status) {
return this.statusMap[status] || '未知状态';
},
//
getStatusTagClass(status) {
switch(status) {
case 10: return 'cata_man'; // -
case 20: return 'cata_warn'; // -
case 30: return 'cata_succ'; // -
case 40: return 'cata_def'; // -
case 50: return 'cata_dag'; // -
default: return 'cata_def';
}
},
//
getEducationText(edu) {
return this.educationMap[edu] || '未知学历';
//
getAvatarBgColor(role) {
return this.roleColors[role] || '#165DFF';
}
}
};
</script>
</script>
<style scoped>
/* 全局变量定义 */
:root {
--primary: #165DFF;
--success: #00B42A;
--gray-50: #F9FAFB;
--gray-100: #F3F4F6;
--gray-200: #E5E7EB;
--gray-400: #9CA3AF;
--gray-500: #6B7280;
--gray-800: #1F2937;
}
/* 容器布局 */
.container {
height: 100vh;
display: flex;
flex-direction: column;
}
/* 头部样式 */
.main-header {
position: sticky;
top: 0;
z-index: 10;
background-color: white;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
padding: 12px 16px;
}
.search-container {
align-items: center;
}
.search-icon {
position: absolute;
left: 12px;
top: 50%;
transform: translateY(-50%);
font-size: 14px;
}
.pl-12 {
padding-left: 2.5rem; /* 缩小内边距 */
}
/* 按钮样式(整体缩小) */
.btn {
border-radius: 8px;
font-weight: 500;
transition: all 0.3s ease;
font-size: 14px;
}
.btn-primary {
background-color: var(--primary);
color: white;
padding: 6px 12px; /* 缩小内边距 */
border: none;
}
.btn-primary:hover {
background-color: #0E4BDB;
}
.btn-success {
background-color: var(--success);
color: white;
border: none;
}
.btn-sm {
padding: 6px 12px; /* 小按钮内边距 */
font-size: 13px;
}
.btn-success:hover {
background-color: #009A22;
}
/* 状态筛选栏(缩小整体尺寸) */
.status-filter {
position: sticky;
top: 52px; /* 对应头部缩小后的高度 */
z-index: 9;
background-color: white;
border-bottom: 1px solid var(--gray-200);
padding: 12px 16px;
}
.scrollbar-hide::-webkit-scrollbar {
display: none;
}
.status-btn {
border: none;
font-weight: 500;
border-radius: 999px;
padding: 4px 12px; /* 缩小按钮内边距 */
margin-right: 8px; /* 缩小按钮间距 */
transition: all 0.3s ease;
font-size: 13px; /* 缩小文字尺寸 */
}
.status-btn-default {
background-color: var(--gray-50);
color: var(--gray-500);
}
.status-btn-default:hover {
background-color: var(--gray-100);
}
/* 选中态样式(清晰可见,不冲突) */
.status-btn-active {
background-color: #165DFF !important;
color: #FFFFFF !important;
}
/* 成员列表(缩小内边距) */
.main {
flex: 1;
background-color: var(--gray-50);
padding: 16px; /* 缩小内边距 */
}
.grid3 {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px; /* 缩小网格间距 */
}
/* 成员卡片(缩小整体尺寸) */
.member-card {
width: 100%;
box-sizing: border-box;
background-color: white;
border-radius: 10px; /* 缩小圆角 */
overflow: hidden;
transition: all 0.3s ease;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
.member-card:hover {
box-shadow: 0 6px 16px -5px rgba(0, 0, 0, 0.05), 0 4px 8px -6px rgba(0, 0, 0, 0.03);
transform: translateY(-1px); /* 缩小hover上移距离 */
}
/* 头像区域(缩小尺寸) */
.card-avatar {
width: 100%;
background: linear-gradient(to bottom, #EFF6FF, #EEF2FF);
padding: 12px 0; /* 缩小内边距 */
text-align: center;
}
.avatar-placeholder {
width: 4rem; /* 缩小头像尺寸 */
height: 4rem;
border-radius: 50%;
border: 3px solid white;
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
display: flex;
align-items: center;
justify-content: center;
font-size: 1rem; /* 缩小文字尺寸 */
}
/* 成员信息(缩小内边距和文字尺寸) */
.card-info {
padding: 16px 12px; /* 缩小内边距 */
text-align: center;
}
.member-name {
font-size: 1rem; /* 缩小姓名文字 */
font-weight: 700;
color: var(--gray-800);
margin-bottom: 4px; /* 缩小间距 */
line-height: 1.3;
}
.member-title {
font-size: 12px; /* 缩小头衔文字 */
color: var(--gray-500);
margin-bottom: 8px; /* 缩小间距 */
line-height: 1.2;
}
/* 无数据提示(缩小尺寸) */
.empty-tip {
padding: 32px 16px; /* 缩小内边距 */
width: 80%;
margin: 0 auto;
background-color: white;
border-radius: 10px; /* 缩小圆角 */
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
text-align: center;
}
.empty-icon {
width: 3rem; /* 缩小图标尺寸 */
height: 3rem;
border-radius: 50%;
background-color: var(--gray-100);
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 12px; /* 缩小间距 */
font-size: 1rem; /* 缩小图标文字 */
}
/* 滚动加载(缩小尺寸) */
.load-more {
margin-top: 16px; /* 缩小间距 */
font-size: 12px; /* 缩小文字尺寸 */
color: var(--gray-400);
text-align: center;
}
/* 聚焦样式 */
.focus-border-primary:focus {
border-color: var(--primary);
}
.focus-ring-1:focus {
box-shadow: 0 0 0 1px var(--primary);
}
/* 文本颜色辅助类(新增小尺寸文字类) */
.text-white {
color: #FFFFFF;
}
.text-gray-400 {
color: #9CA3AF;
}
.text-gray-500 {
color: #6B7280;
}
.text-gray-800 {
color: #1F2937;
}
.text-xl {
font-size: 1.25rem;
}
.text-lg {
font-size: 1rem;
}
.text-base {
font-size: 0.875rem;
}
.text-sm {
font-size: 0.75rem;
}
.text-xs {
font-size: 0.625rem;
}
/* 内边距辅助类(缩小尺寸) */
.px2 {
padding-left: 0.5rem;
padding-right: 0.5rem;
}
.py1 {
padding-top: 0.25rem;
padding-bottom: 0.25rem;
}
.mb1 {
margin-bottom: 0.25rem;
}
.mb2 {
margin-bottom: 0.5rem;
}
</style>

View File

@ -2509,7 +2509,7 @@ export default {
(1000 + Math.round(Math.random() * 8000)) + '.' + fext.toLowerCase();
if (opn.stor == '/') {
var ufparam = {};
ufparam.url = opn.action + "upload&pathfile=" + pathfile;
ufparam.url = opn.action + "upload?pathfile=" + pathfile;
ufparam.header = header;
if (opn.post)
ufparam.formData = opn.post;
@ -2532,7 +2532,7 @@ export default {
fn.success(jsonup.url, gf);
} else {
var [err, s3json] = await this.go(this.callfunc({
func: opn.action + "s3&pathfile=" + pathfile +
func: opn.action + "s3?pathfile=" + pathfile +
"&storselect=" + opn.stor
}));
if (err)

View File

@ -11,7 +11,7 @@
Target Server Version : 100510
File Encoding : 65001
Date: 26/01/2026 15:46:45
Date: 29/01/2026 17:03:51
*/
SET NAMES utf8mb4;
@ -22,12 +22,13 @@ SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
DROP TABLE IF EXISTS `lab_user`;
CREATE TABLE `lab_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '成员id',
`laborgid` bigint(20) NOT NULL COMMENT '所属机构,DB,lab_orgbase',
`icon` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '头像,IMG1',
`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '姓名',
`usertitle` int(11) NOT NULL COMMENT '头衔,CATA,usertitle',
`education` int(11) NOT NULL COMMENT '学历,CATA,education',
`sn` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '编号',
`sn` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '编号',
`sex` int(11) NOT NULL COMMENT '性别,CATA,sex',
`addtimes` bigint(20) NOT NULL COMMENT '加入日期,DATE,Y-m-d',
`mobile` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '手机号,MSK,****',
@ -35,18 +36,29 @@ CREATE TABLE `lab_user` (
`password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT ',密码',
`stpstatus` int(11) NOT NULL COMMENT '|状态|,CATA,stpstatus',
`userlevel` int(11) NOT NULL COMMENT '|等级|,CATA,userlevel',
`totalpnt` bigint(20) NOT NULL COMMENT '总积分|,INT',
`upid` int(11) NOT NULL DEFAULT 0 COMMENT '分享人,DB,ep_user',
`dvotecnt` bigint(20) NOT NULL COMMENT '互动贡献|,INT,次',
`trytime` int(11) NOT NULL DEFAULT 0 COMMENT ',密码重试次数',
`logintimes` bigint(20) NOT NULL COMMENT '登录时间,DATE',
`sid` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT ',授权码',
`exptimes` bigint(20) NOT NULL COMMENT ',授权过期时间,DATE',
`ip` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '#登录IP,IP',
`role` int(11) NOT NULL COMMENT '成员状态:负责人,科研秘书,在册成员,外部人员,历史人员',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 14 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '实验室成员' ROW_FORMAT = Dynamic;
) ENGINE = InnoDB AUTO_INCREMENT = 25 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '实验室成员' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of lab_user
-- ----------------------------
INSERT INTO `lab_user` VALUES (14, 1, '/lab/lab.png', '张三', 10, 70, 'LAB-2024-001', 20, 1700000000000, '13800138001', 'zhangsan@lab.com', 'd527d1bf5fe6a4361f230af48e36e1c4', 10, 30, 0, 120, 0, 1710000000000, '', 0, '192.168.1.101', 60);
INSERT INTO `lab_user` VALUES (15, 1, '/lab/lab.png', '李四', 50, 50, 'LAB-2024-002', 20, 1702000000000, '13900139002', 'lisi@lab.com', 'd527d1bf5fe6a4361f230af48e36e1c4', 10, 20, 0, 86, 11, 1769417475, '', 0, '192.168.1.102', 60);
INSERT INTO `lab_user` VALUES (16, 2, '/lab/lab.png', '王五', 80, 60, 'LAB-2024-003', 20, 1704000000000, '13700137003', 'wangwu@lab.com', 'd527d1bf5fe6a4361f230af48e36e1c4', 10, 10, 0, 45, 2, 1712000000000, '', 0, '192.168.1.103', 60);
INSERT INTO `lab_user` VALUES (17, 1, '/lab/lab.png', '赵六', 20, 60, 'LAB-2024-004', 10, 1706000000000, '13600136004', 'zhaoliu@lab.com', 'd527d1bf5fe6a4361f230af48e36e1c4', 10, 20, 0, 98, 0, 1713000000000, '', 0, '192.168.1.104', 60);
INSERT INTO `lab_user` VALUES (19, 0, '/lab/lab.png', ':3780', 0, 0, '', 90, 1769439652, '18248103780', '', 'd527d1bf5fe6a4361f230af48e36e1c4', 10, 10, 0, 0, 0, 1769671619, 'TM15jZmrBs', 1769675219, '127.0.0.1', 60);
INSERT INTO `lab_user` VALUES (20, 0, '/lab/lab.png', ':0022', 0, 0, '', 90, 1769445509, '13654880022', '', 'd527d1bf5fe6a4361f230af48e36e1c4', 10, 10, 0, 0, 0, 1769448199, 'p43i428v1j', 1769451799, '127.0.0.1', 60);
INSERT INTO `lab_user` VALUES (21, 0, '/lab/lab.png', ':0023', 0, 0, '', 90, 1769529209, '13654880023', '', 'd527d1bf5fe6a4361f230af48e36e1c4', 10, 10, 0, 0, 0, 1769529209, 'FuBzvhAHI5', 1769532809, '127.0.0.1', 60);
INSERT INTO `lab_user` VALUES (22, 0, '/lab/lab.png', ':3788', 0, 0, '', 90, 1769671931, '18248103788', '', 'd527d1bf5fe6a4361f230af48e36e1c4', 10, 10, 0, 0, 0, 1769672290, 'GjXvuqnEoM', 1769675890, '127.0.0.1', 60);
INSERT INTO `lab_user` VALUES (23, 0, '/lab/lab.png', ':3789', 0, 0, '', 90, 1769672770, '18248103789', '', 'd527d1bf5fe6a4361f230af48e36e1c4', 10, 10, 0, 0, 0, 1769674212, 'i49npHbGlL', 1769677812, '127.0.0.1', 60);
INSERT INTO `lab_user` VALUES (24, 0, '/lab/lab.png', ':0024', 0, 0, '', 90, 1769676173, '13654880024', '', 'd527d1bf5fe6a4361f230af48e36e1c4', 10, 10, 0, 0, 0, 1769676173, '1ZPWhJNtkd', 1769679773, '127.0.0.1', 60);
SET FOREIGN_KEY_CHECKS = 1;

View File

@ -42,7 +42,7 @@ function verifyuser($post = null) {
$auth = json_decode(encrypt($ciyauth, 'D', $_token['salt']), true);
if ($auth == null)
return null;
$csql = new \ciy\sql('ap_user'); //弃用redis集群
$csql = new \ciy\sql('lab_user'); //弃用redis集群
$csql->where('id', $auth['id']);
$userrow = $db->getone($csql);
if (!is_array($userrow))
@ -58,7 +58,7 @@ function verifyuser($post = null) {
if ($upid > 0) {
$updata = array();
$updata['upid'] = $upid;
$csql = new \ciy\sql('ap_user');
$csql = new \ciy\sql('lab_user');
$csql->where('id', $userrow['id']);
$db->update($csql, $updata);
}
@ -68,7 +68,7 @@ function verifyuser($post = null) {
$exp = time() + $_token['swapsec'];
$sid = randstr(10);
$auth['_s'] = $sid;
if ($db->execute('update ap_user set exptimes=?,sid=? where id=?', array($exp, $sid, $auth['id'])) === false)
if ($db->execute('update lab_user set exptimes=?,sid=? where id=?', array($exp, $sid, $auth['id'])) === false)
return null;
$authstr = json_encode($auth, JSON_PARTIAL_OUTPUT_ON_ERROR);
$enauth = encrypt($authstr, 'E', $_token['salt']);

View File

@ -134,6 +134,7 @@ class login {
$rsuser['education'] = 0;
$rsuser['sn'] = '';
$rsuser['sex'] = 90;
$rsuser['role']=60;
$rsuser['stpstatus'] = 10;
$rsuser['userlevel'] = 10;
$rsuser['mobile'] = $user;
@ -388,7 +389,6 @@ class login {
$ret['pnttrack'] = $db->get($csql);
$ret['me'] = array();
$ret['me']['addtimes'] = $userrow['addtimes'];
$ret['me']['accounttimes'] = $userrow['accounttimes'];
//$ret['me']['saasid_a'] = $userrow['saasid_a'];
$ret['me']['id'] = $userrow['id'];
$ret['me']['eid'] = enid($userrow['id']);
@ -397,31 +397,8 @@ class login {
$ret['me']['mobile'] = $userrow['mobile'];
$ret['me']['name'] = $userrow['name'];
$ret['me']['userlevel'] = $userrow['userlevel'];
$ret['me']['mymoney'] = $userrow['mymoney'];
$ret['me']['mycashmoney'] = $userrow['mycashmoney'];
$ret['me']['myinvmoney'] = $userrow['myinvmoney'];
$ret['me']['mybondmoney'] = $userrow['mybondmoney'];
$ret['me']['mypnt'] = $userrow['mypnt'];
$ret['me']['certs'] = $userrow['certs'];
$ret['me']['needpass'] = empty($userrow['password']);
$ret['me']['cciy'] = '';
$csql = new \ciy\sql('ap_usr_ext');
$csql->where('id', $userrow['id']);
$extrow = $db->getone($csql);
if (is_array($extrow)) {
$ret['me']['truename'] = $extrow['truename'];
$ret['me']['email'] = $extrow['email'];
$ret['me']['wxno'] = $extrow['wxno'];
$ret['me']['idid'] = $extrow['idid'];
$ret['me']['cciy'] = $extrow['cciy'] . '';
$ret['me']['cashtype'] = $extrow['cashtype'];
$ret['me']['bankno'] = $extrow['bankno'];
$ret['me']['bankname'] = $extrow['bankname'];
$ret['me']['bankaccount'] = $extrow['bankaccount'];
$ret['me']['bankcode'] = $extrow['bankcode'];
} else {
$ret['me']['truename'] = '';
}
return succjson($ret);
}
public static function json_logout() {

View File

@ -1,313 +1,342 @@
<?php
namespace web\ambap;
// 确保引入必要的工具类(根据实际项目路径调整)
require_once dirname(__FILE__) . '/../../ciy/db.php';
require_once dirname(__FILE__) . '/../../ciy/post.php';
require_once dirname(__FILE__) . '/../../ciy/sql.php';
/**
* 依赖:全局$db对象、ciy\post、errjson/succjson、savelog、tostamp函数
*/
class member {
// 接口入口:通过 act 参数路由到不同方法
public static function index() {
// 1. 解决跨域问题
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: POST, GET, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, X-Requested-With");
header("Content-Type: application/json; charset=utf-8");
// 处理 OPTIONS 预检请求
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
exit;
}
// 2. 安全获取 act 参数
$act = isset($_REQUEST['act']) ? trim($_REQUEST['act']) : '';
switch($act) {
case 'member.list':
self::json_list();
break;
case 'member.add':
self::json_add();
break;
case 'member.edit':
self::json_edit();
break;
case 'member.detail':
self::json_detail();
break;
case 'member.del':
self::json_del();
break;
case 'member.audit':
self::json_audit();
break;
default:
self::err('无效的接口动作:' . $act);
break;
}
}
// 1. 获取成员列表(适配当前 db.php 的 ciy\sql 语法)
public static function json_list() {
/**
* 获取成员列表(支持分页/筛选/字典值转换)
* @return array
*/
public static function json_get_list() {
global $db;
$post = new \ciy\post();
$page = $post->getint('page', 1);
$pageSize = $post->getint('pageSize', 15);
$offset = ($page - 1) * $pageSize;
// 构建查询条件(通过 ciy\sql 的构造参数指定字段)
$csql = new \ciy\sql('lab_user', 'id, name, mobile, usertitle, stpstatus, sex, education, email, avatar, addtimes');
$csql->limit($offset, $pageSize); // 分页:偏移量、每页条数
$csql->order('addtimes DESC');
// 核心修改:使用 db.php 的 get 方法($rowcount=-1 自动查总数)
$rowcount = -1;
$list = $db->get($csql, $rowcount);
// 处理查询失败
// 1. 初始化参数(解决未定义变量)
$page = $post->getint('page', 1); // 当前页
$pagesize = $post->getint('pagesize', 20); // 每页条数
$offset = ($page - 1) * $pagesize; // 偏移量
$mobile = trim($post->get('mobile', '')); // 手机号筛选
$name = trim($post->get('name', '')); // 姓名筛选
$status = $post->getint('status', 0); // 状态筛选
$role = $post->getint('role', 60); // 角色筛选
// 2. 构建SQL修复精简JOIN语句格式避免语法错误
$csql = new \ciy\sql("lab_user u
LEFT JOIN zc_cata s ON u.sex = s.codeid AND s.cbid = 10
LEFT JOIN zc_cata st ON u.stpstatus = st.codeid AND st.cbid = 11
LEFT JOIN zc_cata e ON u.education = e.codeid AND e.cbid = 102
LEFT JOIN zc_cata ut ON u.usertitle = ut.codeid AND ut.cbid = 12001701
LEFT JOIN zc_cata r ON u.role = r.codeid AND r.cbid = 12001703
LEFT JOIN zc_cata rr ON u.userlevel=rr.codeid AND rr.cbid=12001704");
// 配置查询字段(含字典关联中文名称)
$csql->column("u.id, u.mobile, u.name, u.usertitle, u.education, u.sex,
u.role, u.userlevel, u.stpstatus, u.addtimes, u.logintimes, u.ip,
s.name as sex_name, st.name as stpstatus_name, e.name as education_name,
ut.name as usertitle_name, r.name as role_name, rr.name as userlevel_name");
// 3. 拼接筛选条件
if (!empty($mobile)) {
$csql->where('u.mobile', $mobile);
}
if (!empty($name)) {
$csql->where('u.name', '%' . $name . '%', 'like');
}
if ($status > 0) {
$csql->where('u.stpstatus', $status);
}
if ($role > 0) {
$csql->where('u.role', $role);
}
// 4. 排序+分页(正确传参)
$csql->order('u.logintimes desc');
$csql->limit($offset, $pagesize);
// 5. 执行查询
$list = $db->get($csql);
if ($list === false) {
return self::err('获取列表失败:' . $db->error);
return errjson('查询成员列表失败: ' . $db->error);
}
// 格式化返回数据
$retList = [];
foreach ($list as $item) {
$retList[] = [
'id' => intval($item['id']),
'name' => $item['name'] ?? '',
'mobile' => $item['mobile'] ?? '',
'usertitle' => intval($item['usertitle']),
'stpstatus' => intval($item['stpstatus']),
'sex' => intval($item['sex']),
'education' => intval($item['education'] ?? 50),
'email' => $item['email'] ?? '',
'avatar' => $item['avatar'] ?? '/static/avatar-default.png',
'addtimes' => intval($item['addtimes'])
];
}
echo json_encode([
'code' => 1,
'list' => $retList,
'total' => $rowcount,
// 6. 查询总数修复移除limit方法而非传0,0
$countSql = clone $csql;
$countSql->column('count(DISTINCT u.id) as total');
// 关键修复删除limit(0,0)ciy\sql默认无limit
$total = $db->get1($countSql);
// 7. 返回结果
return succjson([
'list' => $list ?: [],
'total' => $total ?: 0,
'page' => $page,
'pageSize' => $pageSize
], JSON_UNESCAPED_UNICODE);
'pagesize' => $pagesize
]);
}
// 2. 新增成员(适配当前 db.php
public static function json_add() {
/**
* 获取成员详情(含字典中文名称)
* @return array
*/
public static function json_get_detail() {
global $db;
$post = new \ciy\post();
$id = $post->getint('id');
// 获取表单数据
$name = trim($post->get('name', ''));
$mobile = trim($post->get('mobile', ''));
$usertitle = $post->getint('usertitle', 10);
$stpstatus = $post->getint('stpstatus', 30);
$sex = $post->getint('sex', 90);
$education = $post->getint('education', 50);
$email = trim($post->get('email', ''));
$password = trim($post->get('password', ''));
// 基础验证
if (empty($name)) return self::err('请输入姓名');
if (empty($mobile)) return self::err('请输入手机号');
if (!preg_match('/^1[3-9]\d{9}$/', $mobile)) return self::err('手机号格式错误');
if (empty($password)) return self::err('请设置密码');
if (strlen($password) < 6) return self::err('密码长度不少于6位');
// 检查手机号是否已注册
$csql = new \ciy\sql('lab_user', 'id');
$csql->where('mobile', $mobile);
$exist = $db->getone($csql);
if (is_array($exist)) return self::err('该手机号已注册:' . $mobile);
// 组装数据
$data = [
'name' => $name,
'mobile' => $mobile,
'usertitle' => $usertitle,
'stpstatus' => $stpstatus,
'sex' => $sex,
'education' => $education,
'email' => $email,
'password' => $password,
'userlevel' => 10,
'trytime' => 0,
'logintimes' => self::tostamp(),
'addtimes' => self::tostamp(),
'ip' => self::getip(),
'laborgid' => 1,
'sn' => 'LAB-' . date('Ymd') . '-' . rand(1000, 9999),
'totalpnt' => 0,
'dvotecnt' => 0,
'updatetime' => self::tostamp()
];
// 插入数据
$csql = new \ciy\sql('lab_user');
$insertId = $db->insert($csql, $data);
if ($insertId === false) {
return self::err('新增失败:' . $db->error);
if ($id <= 0) {
return errjson('请传入有效的成员ID');
}
echo json_encode([
'code' => 1,
'msg' => '新增成功',
'id' => $insertId
], JSON_UNESCAPED_UNICODE);
// 修复精简JOIN语句格式
$csql = new \ciy\sql("lab_user u
LEFT JOIN zc_cata s ON u.sex = s.codeid AND s.cbid = 10
LEFT JOIN zc_cata st ON u.stpstatus = st.codeid AND st.cbid = 11
LEFT JOIN zc_cata e ON u.education = e.codeid AND e.cbid = 102
LEFT JOIN zc_cata ut ON u.usertitle = ut.codeid AND ut.cbid = 12001701
LEFT JOIN zc_cata r ON u.role = r.codeid AND r.cbid = 12001703
LEFT JOIN zc_cata rr ON u.userlevel=rr.codeid AND rr.cbid=12001704");
$csql->column("u.*, s.name as sex_name, st.name as stpstatus_name,
e.name as education_name, ut.name as usertitle_name,
r.name as role_name, rr.name as userlevel_name");
$csql->where('u.id', $id);
$detail = $db->getone($csql);
if ($detail === false) {
return errjson('查询成员详情失败: ' . $db->error);
}
if (!is_array($detail)) {
return errjson('该成员不存在');
}
// 补充扩展信息(单表直接传构造函数)
$extSql = new \ciy\sql('ap_usr_ext');
$extSql->where('id', $id);
$extDetail = $db->getone($extSql);
if (is_array($extDetail)) {
$detail['appcid'] = $extDetail['appcid'];
}
return succjson($detail);
}
// 3. 编辑成员(适配当前 db.php
/**
* 编辑成员信息
* @return array
*/
public static function json_edit() {
global $db;
$post = new \ciy\post();
// 参数校验
$id = $post->getint('id');
$name = trim($post->get('name', ''));
$usertitle = $post->getint('usertitle', 10);
$stpstatus = $post->getint('stpstatus', 30);
$usertitle = $post->getint('usertitle', 0);
$education = $post->getint('education', 0);
$sex = $post->getint('sex', 90);
$education = $post->getint('education', 50);
$email = trim($post->get('email', ''));
// 参数验证
if (empty($id)) return self::err('参数错误缺少成员ID');
if (empty($name)) return self::err('请输入姓名');
// 优化role默认值改为0避免无字典数据
$role = $post->getint('role', 0);
$userlevel = $post->getint('userlevel', 10);
if ($id <= 0) {
return errjson('请传入有效的成员ID');
}
if (empty($name)) {
return errjson('请填写成员姓名');
}
// 组装更新数据
$data = [
$updata = [
'name' => $name,
'usertitle' => $usertitle,
'stpstatus' => $stpstatus,
'sex' => $sex,
'education' => $education,
'email' => $email,
'updatetime' => self::tostamp()
'sex' => $sex,
'role' => $role,
'userlevel' => $userlevel
];
// 更新数据
// 单表操作:构造函数直接传表名
$csql = new \ciy\sql('lab_user');
$csql->where('id', $id);
$result = $db->update($csql, $data);
if ($result === false) {
return self::err('修改失败:' . $db->error);
$res = $db->update($csql, $updata);
if ($res === false) {
savelog($db, 0, 'MEMBEREDIT', '编辑成员[' . $id . ']失败: ' . $db->error);
return errjson('编辑成员失败: ' . $db->error);
}
echo json_encode([
'code' => 1,
'msg' => '修改成功'
], JSON_UNESCAPED_UNICODE);
savelog($db, $id, 'MEMBEREDIT', '编辑成员[' . $id . ']成功');
return succjson(['msg' => '编辑成功']);
}
// 4. 获取成员详情(适配当前 db.php
public static function json_detail() {
/**
* 禁用/启用成员
* @return array
*/
public static function json_change_status() {
global $db;
$post = new \ciy\post();
$id = $post->getint('id');
if (empty($id)) return self::err('参数错误缺少成员ID');
// 查询详情(通过 ciy\sql 构造参数指定字段)
$csql = new \ciy\sql('lab_user', 'id, name, mobile, usertitle, stpstatus, sex, education, email');
$status = $post->getint('status');
if ($id <= 0) {
return errjson('请传入有效的成员ID');
}
$updata = ['stpstatus' => $status];
$csql = new \ciy\sql('lab_user');
$csql->where('id', $id);
$item = $db->getone($csql);
$res = $db->update($csql, $updata);
if ($res === false) {
$action = $status == 10 ? '启用' : '禁用';
savelog($db, 0, 'MEMBERSTATUS', $action . '成员[' . $id . ']失败: ' . $db->error);
return errjson($action . '成员失败: ' . $db->error);
}
$action = $status == 10 ? '启用' : '禁用';
savelog($db, $id, 'MEMBERSTATUS', $action . '成员[' . $id . ']成功');
return succjson(['msg' => $action . '成功']);
}
if (!is_array($item)) return self::err('成员不存在或已删除');
// 格式化返回数据
$data = [
'id' => intval($item['id']),
'name' => $item['name'] ?? '',
'mobile' => $item['mobile'] ?? '',
'usertitle' => intval($item['usertitle']),
'stpstatus' => intval($item['stpstatus']),
'sex' => intval($item['sex']),
'education' => intval($item['education'] ?? 50),
'email' => $item['email'] ?? ''
/**
* 逻辑删除成员标记deletetimes
* @return array
*/
public static function json_delete() {
global $db;
$post = new \ciy\post();
$id = $post->getint('id');
if ($id <= 0) {
return errjson('请传入有效的成员ID');
}
// 逻辑删除:标记删除时间+禁用状态
$updata = [
'stpstatus' => 99, // 99:已删除
'deletetimes' => tostamp() // 时间戳
];
echo json_encode([
'code' => 1,
'data' => $data
], JSON_UNESCAPED_UNICODE);
}
// 5. 删除成员(适配当前 db.php
public static function json_del() {
global $db;
$post = new \ciy\post();
$id = $post->getint('id');
if (empty($id)) return self::err('参数错误缺少成员ID');
// 物理删除
$csql = new \ciy\sql('lab_user');
$csql->where('id', $id);
$result = $db->delete($csql);
if ($result === false) {
return self::err('删除失败:' . $db->error);
$res = $db->update($csql, $updata);
if ($res === false) {
savelog($db, 0, 'MEMBERDELETE', '删除成员[' . $id . ']失败: ' . $db->error);
return errjson('删除成员失败: ' . $db->error);
}
echo json_encode([
'code' => 1,
'msg' => '删除成功'
], JSON_UNESCAPED_UNICODE);
savelog($db, $id, 'MEMBERDELETE', '删除成员[' . $id . ']成功');
return succjson(['msg' => '删除成功']);
}
// 6. 审核成员(适配当前 db.php
public static function json_audit() {
/**
* 重置成员密码(和登录逻辑一致)
* @return array
*/
public static function json_reset_pass() {
global $db;
global $_token; // 确保全局_token包含salt
$post = new \ciy\post();
$id = $post->getint('id');
$status = $post->getint('status');
if (empty($id)) return self::err('参数错误缺少成员ID');
if (!in_array($status, [10,20,30,40,50])) return self::err('无效的状态值');
$data = ['stpstatus' => $status];
$newPass = trim($post->get('new_pass', ''));
if ($id <= 0) {
return errjson('请传入有效的成员ID');
}
if (empty($newPass)) {
return errjson('请填写新密码');
}
// 优化:校验$_token是否存在
if (empty($_token) || empty($_token['salt'])) {
return errjson('密码加密配置异常,请联系管理员');
}
// 密码加密(和注册逻辑对齐)
$encryptPass = md5($newPass . $_token['salt']);
$updata = [
'password' => $encryptPass,
'trytime' => 0 // 重置错误尝试次数
];
$csql = new \ciy\sql('lab_user');
$csql->where('id', $id);
$result = $db->update($csql, $data);
if ($result === false) {
return self::err('审核失败:' . $db->error);
$res = $db->update($csql, $updata);
if ($res === false) {
savelog($db, 0, 'MEMBERRESETPASS', '重置成员[' . $id . ']密码失败: ' . $db->error);
return errjson('重置密码失败: ' . $db->error);
}
echo json_encode([
'code' => 1,
'msg' => '审核成功'
], JSON_UNESCAPED_UNICODE);
savelog($db, $id, 'MEMBERRESETPASS', '重置成员[' . $id . ']密码成功');
return succjson(['msg' => '重置密码成功']);
}
// 通用错误返回
private static function err($msg) {
echo json_encode([
'code' => 0,
'errmsg' => $msg
], JSON_UNESCAPED_UNICODE);
exit;
}
// 兼容tostamp函数
private static function tostamp() {
return isset($GLOBALS['tostamp']) ? $GLOBALS['tostamp']() : time() * 1000;
}
// 兼容getip函数
private static function getip() {
if (isset($_SERVER['HTTP_X_REAL_IP'])) {
return $_SERVER['HTTP_X_REAL_IP'];
} elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
return $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
return $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1';
/**
* 获取字典表数据(用于前端下拉选项)
* @return array
*/
public static function json_get_cata() {
global $db;
$post = new \ciy\post();
$cbid = $post->getint('cbid');
if ($cbid <= 0) {
return errjson('请传入有效的字典分类ID');
}
$csql = new \ciy\sql('zc_cata');
$csql->where('cbid', $cbid);
$csql->order('codeid asc');
$list = $db->get($csql);
if ($list === false) {
return errjson('查询字典失败: ' . $db->error);
}
return succjson(['list' => $list ?: []]);
}
}
global $db;
$post = new \ciy\post();
$action = trim($post->get('action', ''));
$result = [];
// 执行入口
member::index();
?>
// 根据action调用对应方法
switch ($action) {
case 'get_list':
$result = member::json_get_list();
break;
case 'get_detail':
$result = member::json_get_detail();
break;
case 'edit':
$result = member::json_edit();
break;
case 'change_status':
$result = member::json_change_status();
break;
case 'delete':
$result = member::json_delete();
break;
case 'reset_pass':
$result = member::json_reset_pass();
break;
case 'get_cata':
$result = member::json_get_cata();
break;
default:
$result = errjson('无效的接口操作: ' . $action);
}
// 输出JSON结果前端才能接收数据
header('Content-Type: application/json; charset=utf-8');
echo json_encode($result, JSON_UNESCAPED_UNICODE);
exit;

BIN
web/ud/lab/lab.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@ -11,7 +11,7 @@
Target Server Version : 100510
File Encoding : 65001
Date: 26/01/2026 16:22:50
Date: 29/01/2026 19:33:39
*/
SET NAMES utf8mb4;
@ -33,14 +33,14 @@ CREATE TABLE `zc_cata` (
`extdata` varchar(180) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '扩展值',
PRIMARY KEY (`id`) USING BTREE,
INDEX `cbid`(`cbid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 12001749 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '字典表' ROW_FORMAT = Dynamic;
) ENGINE = InnoDB AUTO_INCREMENT = 12001750 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '字典表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of zc_cata
-- ----------------------------
INSERT INTO `zc_cata` VALUES (1, 0, 0, 1, 0, '', '固定字典', '', '');
INSERT INTO `zc_cata` VALUES (2, 0, 0, 1, 0, '', '灵活字典', '', '');
INSERT INTO `zc_cata` VALUES (10, 1, 0, 1, 0, 'sex', '性别', '', 'zc_admin');
INSERT INTO `zc_cata` VALUES (10, 1, 0, 1, 0, 'sex', '性别', '', 'zc_admin\nlab_user');
INSERT INTO `zc_cata` VALUES (11, 1, 0, 1, 0, 'stpstatus', '账号状态', '', 'zc_admin');
INSERT INTO `zc_cata` VALUES (12, 1, 0, 1, 0, 'auditstatus', '审核状态', '', 'zc_urole');
INSERT INTO `zc_cata` VALUES (13, 1, 0, 1, 0, 'autotaskstatus', '任务状态', '', 'zc_autotask');
@ -126,12 +126,8 @@ INSERT INTO `zc_cata` VALUES (10252, 0, 10, 1, 103, '30', '转账中', '', '');
INSERT INTO `zc_cata` VALUES (10253, 0, 10, 1, 103, '100', '成功', '', '');
INSERT INTO `zc_cata` VALUES (10254, 0, 10, 1, 103, '90', '失败', '', '');
INSERT INTO `zc_cata` VALUES (12001701, 2, 10, 1, 0, 'usertitle', '实验室成员头衔', '', 'lab_user.usertitle');
INSERT INTO `zc_cata` VALUES (12001702, 2, 20, 1, 0, 'sex', '成员性别', '', 'lab_user.sex');
INSERT INTO `zc_cata` VALUES (12001703, 2, 30, 1, 0, 'stpstatus', '实验室成员状态', '', 'lab_user.stpstatus');
INSERT INTO `zc_cata` VALUES (12001703, 2, 30, 1, 0, 'role', '实验室成员状态', '', 'lab_user.role');
INSERT INTO `zc_cata` VALUES (12001704, 2, 40, 1, 0, 'userlevel', '实验室成员等级', '', 'lab_user.userlevel');
INSERT INTO `zc_cata` VALUES (12001724, 0, 10, 1, 12001702, '10', '未知', 'weizhi', '');
INSERT INTO `zc_cata` VALUES (12001725, 0, 20, 1, 12001702, '20', '', 'man', '');
INSERT INTO `zc_cata` VALUES (12001726, 0, 30, 1, 12001702, '30', '', 'woman', '');
INSERT INTO `zc_cata` VALUES (12001729, 0, 10, 1, 12001703, '10', '负责人', 'man', '');
INSERT INTO `zc_cata` VALUES (12001730, 0, 20, 1, 12001703, '20', '科研秘书', 'man', '');
INSERT INTO `zc_cata` VALUES (12001731, 0, 30, 1, 12001703, '30', '在册成员', 'succ', '');
@ -149,5 +145,6 @@ INSERT INTO `zc_cata` VALUES (12001745, 0, 10, 1, 12001704, '10', '普通成员'
INSERT INTO `zc_cata` VALUES (12001746, 0, 20, 1, 12001704, '20', '核心成员', '', '');
INSERT INTO `zc_cata` VALUES (12001747, 0, 30, 1, 12001704, '30', '管理员', '', '');
INSERT INTO `zc_cata` VALUES (12001748, 0, 10, 1, 102, '0', '未知', '', '');
INSERT INTO `zc_cata` VALUES (12001749, 0, 60, 1, 12001703, '60', '未知', 'weizhi', '');
SET FOREIGN_KEY_CHECKS = 1;