commit 7ff2185f8e7c314e46db450361225fe54c83f269 Author: Jerry Yan <792602257@qq.com> Date: Sat Jun 21 16:39:10 2025 +0800 Init diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..59c5fc3 --- /dev/null +++ b/composer.json @@ -0,0 +1,39 @@ +{ + "name": "q792602257/think-plugs-staff", + "description": "ThinkAdmin Plugin: Staff with Dept Support", + "version": "dev-master", + "minimum-stability": "dev", + "license": "WTFPL", + "authors": [ + { + "name": "Jerry Yan", + "email": "792602257@qq.com" + } + ], + "extra": { + "config": { + "type": "module", + "name": "员工及组织架构模块", + "platforms": ["h5web"], + "description": "员工及组织架构模块" + }, + "plugin": { + "copy": { + "stc/database": "database/migrations" + } + }, + "think": { + "services": [ + "jerryyan\\staff\\Service" + ] + } + }, + "autoload": { + "psr-4": { + "jerryyan\\staff\\": "src" + } + }, + "require": { + + } +} \ No newline at end of file diff --git a/src/Service.php b/src/Service.php new file mode 100644 index 0000000..cf1151e --- /dev/null +++ b/src/Service.php @@ -0,0 +1,24 @@ + '组织架构', + 'subs' => [ + ['name' => '员工管理', 'icon' => 'layui-icon layui-icon-group', 'url' => 'staff/user/index'], + ['name' => '部门管理', 'icon' => 'layui-icon layui-icon-cols', 'url' => 'staff/dept/index'], +// ['name' => '角色管理', 'icon' => 'layui-icon layui-icon-auz', 'url' => 'staff/role/index'], + ] + ] + ]; + } +} \ No newline at end of file diff --git a/src/controller/Dept.php b/src/controller/Dept.php new file mode 100644 index 0000000..cdbb600 --- /dev/null +++ b/src/controller/Dept.php @@ -0,0 +1,120 @@ +layTable(function () { + $this->title = '员工部门'; + }, static function (QueryHelper $query) { + $query->with('parent.parent')->append(['headman_name']); + $query->equal('status')->like('name'); + }); + } + + /** + * 添加部门 + * @auth true + * @menu true + * @return void + */ + public function add() + { + StaffDept::mForm('form'); + } + + /** + * 编辑部门 + * @auth true + * @return void + */ + public function edit() + { + StaffDept::mForm('form'); + } + + protected function _form_filter(array &$data) + { + if ($this->request->isPost()) { + $this->_vali([ + 'name.require' => '部门名称不能为空!', + ]); + } else { + $this->depts = StaffDept::topItems(); + } + } + + /** + * 部门排序 + * @auth true + * @return void + */ + public function sort() + { + StaffDept::mSave($this->_vali([ + 'sort.require' => '排序参数不能为空!', + 'sort.number' => '排序参数格式错误!', + ])); + } + + /** + * 设置部门领导人 + * @auth true + * @return void + */ + public function headman() + { + $data = $this->_vali([ + 'id.require' => '部门不能为空!', + ]); + if ($this->request->isGet()) { + $this->title = '设置部门负责人'; + $this->dept = StaffDept::mk()->where(['id' => $data['id']])->findOrEmpty(); + if ($this->dept->isEmpty()) $this->error('部门不存在!'); + $this->users = StaffUser::mk()->where(['status' => 1, 'dept_id' => $this->dept['id']])->select(); + $this->fetch(); + } else { + StaffDept::mSave($this->_vali([ + 'headman_id.require' => '部门领导人不能为空!', + 'headman_id.number' => '部门领导人格式错误!', + ])); + } + } + + /** + * 启用禁用部门 + * @auth true + * @return void + */ + public function state() + { + StaffDept::mSave($this->_vali([ + 'status.in:0,1' => '状态值范围异常!', + 'status.require' => '状态值不能为空!', + ])); + } + + /** + * 删除部门 + * @auth true + * @return void + */ + public function del() + { + StaffDept::mDelete(); + } + +} \ No newline at end of file diff --git a/src/controller/User.php b/src/controller/User.php new file mode 100644 index 0000000..0fbc5b7 --- /dev/null +++ b/src/controller/User.php @@ -0,0 +1,151 @@ +layTable(function () { + $this->title = '部门员工'; + $this->dept = StaffDept::tree(); + }, static function (QueryHelper $query) { + $query->equal('dept_id,status')->like('name'); + $query->with('dept')->append(['dept_name']); + }); + } + + /** + * 添加员工 + * @auth true + * @menu true + * @return void + */ + public function add() + { + StaffUser::mForm('form'); + } + + /** + * 编辑员工 + * @auth true + * @return void + */ + public function edit() + { + StaffUser::mForm('form'); + } + + protected function _form_filter(array &$data) + { + if ($this->request->isPost()) { + // 检查资料是否完整 + $this->_vali([ + 'name.require' => '用户名称不能为空!', + 'phone.require' => '手机号码不能为空!', + 'phone.mobile' => '手机号码格式错误!', + 'email.email' => '邮箱地址格式错误!', + 'dept_id.require' => '部门名称不能为空!', + 'authorize.require' => '未配置权限!' + ]); + if (!empty($data['password'])) { + if (strlen($data['password']) < 6) { + $this->error('密码长度不能少于6位!'); + }; + } + } else { + if (!empty($data['id'])) { + $user = SystemUser::mk()->find($data['id']); + $data['authorize'] = str2arr($user['authorize'] ?? '');; + } else { + $data['authorize'] = []; + } + $data['dept_id'] = $data['dept_id'] ?? ''; + $this->auths = SystemAuth::items(); + $this->depts = StaffDept::items(); + } + } + + protected function _form_result(bool $state, $data) + { + if ($state) { + $user = []; + // 检查账号是否重复 + $map = ['username' => $data['phone'], 'is_deleted' => 0]; + $systemUser = SystemUser::mk()->where($map)->findOrEmpty(); + if ($systemUser->isExists() && $systemUser->id != $data['id']) { + $this->error("账号已经存在,请使用其它账号!"); + } + $user['id'] = $data['id']; + $user['username'] = $data['phone']; + if (!empty($data['password'])) { + $user['password'] = md5($data['password']); + } + $user['nickname'] = $data['name']; + $user['contact_mail'] = $data['email']; + // 处理上传的权限格式 + $user['authorize'] = arr2str($data['authorize'] ?? []); + SystemUser::mk()->save($user); + } + } + + /** + * 启用禁用员工 + * @return void + */ + public function state() + { + StaffUser::mSave($this->_vali([ + 'status.in:0,1' => '状态值范围异常!', + 'status.require' => '状态值不能为空!', + ])); + } + + protected function _state_save_result(bool $state, $data) + { + if ($state) { + $systemUser = SystemUser::mk()->findOrEmpty($this->request->post('id')); + if ($systemUser->isExists()) { + $systemUser->status = $this->request->post('status'); + $systemUser->save(); + } + } + } + + /** + * 删除员工 + * @auth true + * @return void + */ + public function del() + { + StaffUser::mDelete(); + } + + protected function _del_delete_result(bool $state) + { + if ($state) { + $systemUser = SystemUser::mk()->findOrEmpty($this->request->post('id')); + if ($systemUser->isExists()) { + $systemUser->is_deleted = 1; + $systemUser->save(); + } + } + } +} \ No newline at end of file diff --git a/src/model/StaffDept.php b/src/model/StaffDept.php new file mode 100644 index 0000000..19c86ca --- /dev/null +++ b/src/model/StaffDept.php @@ -0,0 +1,77 @@ + 0])->field("id, name")->select()->unshift([ + 'id' => 0, + 'name' => '顶级部门', + ]); + } + + public function scopeDeleted(Query $query): void + { + $query->where(['is_deleted' => 1]); + } + + public function scopeNotDeleted(Query $query): void + { + $query->where(['is_deleted' => 0]); + } + + public static function items() + { + return self::mk()->where(['status' => 1])->select(); + } + + public function staffs(): HasMany + { + return $this->hasMany(StaffUser::class, 'dept_id', 'id')->where([ + 'status' => 1, 'is_deleted' => 0, + ]); + } + + public function headman(): HasOne + { + return $this->hasOne(StaffUser::class, 'id', 'headman_id')->where([ + 'status' => 1, 'is_deleted' => 0, + ]); + } + + public function children(): HasMany + { + return $this->hasMany(StaffDept::class, 'pid', 'id'); + } + + public function parent(): HasOne + { + return $this->hasOne(StaffDept::class, 'id', 'pid'); + } + + public static function tree() + { + return static::mk()->where(['is_deleted' => 0, 'pid' => 0])->with('children.children')->select(); + } + + public function getHeadmanNameAttr() + { + if (!$this->headman_id) { + return '无'; + } + return $this->headman ? $this->headman['name'] : '无'; + } +} \ No newline at end of file diff --git a/src/model/StaffUser.php b/src/model/StaffUser.php new file mode 100644 index 0000000..f340ac3 --- /dev/null +++ b/src/model/StaffUser.php @@ -0,0 +1,38 @@ +where('is_deleted', '=', 1); + } + + public function scopeNotDeleted(Query $query): void + { + $query->where('is_deleted', '=', 0); + } + + public function dept(): HasOne + { + return $this->hasOne(StaffDept::class, 'id', 'dept_id')->where([ + 'status' => 1, 'is_deleted' => 0, + ]); + } + + public function getDeptNameAttr($value, $data) + { + return $this->dept ? $this->dept['name'] : ''; + } +} \ No newline at end of file diff --git a/src/view/dept/form.html b/src/view/dept/form.html new file mode 100644 index 0000000..f1cfa63 --- /dev/null +++ b/src/view/dept/form.html @@ -0,0 +1,29 @@ +
+
+
+ 基础信息 + + +
+
+ +
+ {notempty name='vo.id'}{/notempty} + +
+ + +
+
\ No newline at end of file diff --git a/src/view/dept/headman.html b/src/view/dept/headman.html new file mode 100644 index 0000000..b1dd2c5 --- /dev/null +++ b/src/view/dept/headman.html @@ -0,0 +1,26 @@ +
+
+
+ 部门:{$dept.name} + +
+
+ +
+ {notempty name='get.id'}{/notempty} + +
+ + +
+
\ No newline at end of file diff --git a/src/view/dept/index.html b/src/view/dept/index.html new file mode 100644 index 0000000..2adc397 --- /dev/null +++ b/src/view/dept/index.html @@ -0,0 +1,89 @@ +{extend name='table'} + +{block name="button"} + + + +{/block} + +{block name="content"} +
+ {include file="dept/index_search"} +
+
+ + + + + +{/block} \ No newline at end of file diff --git a/src/view/dept/index_search.html b/src/view/dept/index_search.html new file mode 100644 index 0000000..42c5555 --- /dev/null +++ b/src/view/dept/index_search.html @@ -0,0 +1,27 @@ +
+ {:lang('条件搜索')} + +
\ No newline at end of file diff --git a/src/view/main.html b/src/view/main.html new file mode 100644 index 0000000..a698dd7 --- /dev/null +++ b/src/view/main.html @@ -0,0 +1,23 @@ +
+ {block name='style'}{/block} + {block name='header'} + {notempty name='title'} +
+ {$title|lang} +
{block name='button'}{/block}
+
+ {/notempty} + {/block} +
+
+
+ {notempty name='showErrorMessage'} +
+ {:lang('系统提示:')}{$showErrorMessage|raw} +
+ {/notempty} + {block name='content'}{/block} +
+
+ {block name='script'}{/block} +
\ No newline at end of file diff --git a/src/view/table.html b/src/view/table.html new file mode 100644 index 0000000..fef73fe --- /dev/null +++ b/src/view/table.html @@ -0,0 +1,23 @@ +
+ {block name='style'}{/block} + {block name='header'} + {notempty name='title'} +
+ {$title|lang} +
{block name='button'}{/block}
+
+ {/notempty} + {/block} +
+
+
+ {notempty name='showErrorMessage'} +
+ {:lang('系统提示:')}{$showErrorMessage|raw} +
+ {/notempty} + {block name='content'}{/block} +
+
+ {block name='script'}{/block} +
\ No newline at end of file diff --git a/src/view/user/form.html b/src/view/user/form.html new file mode 100644 index 0000000..4336c01 --- /dev/null +++ b/src/view/user/form.html @@ -0,0 +1,91 @@ +
+
+
+ 基础信息 + + + + + +
+ +
+ 部门权限 + {if !empty($depts)} +
+
部门
+
+ {foreach $depts as $dept} + + {/foreach} +
+
+ {/if} + {if !empty($auths)} +
+
访问权限
+
+ {foreach $auths as $authorize} + + {/foreach} +
+
+ {/if} +
+
+ +
+ {notempty name='vo.id'}{/notempty} + +
+ + +
+
\ No newline at end of file diff --git a/src/view/user/index.html b/src/view/user/index.html new file mode 100644 index 0000000..30c0949 --- /dev/null +++ b/src/view/user/index.html @@ -0,0 +1,131 @@ +{extend name='table'} + +{block name="button"} + + + + + + +{/block} + +{block name="content"} +
+
+ +
+
+
+ {include file="user/index_search"} +
+
+
+ + + + +{/block} \ No newline at end of file diff --git a/src/view/user/index_search.html b/src/view/user/index_search.html new file mode 100644 index 0000000..b9b34d9 --- /dev/null +++ b/src/view/user/index_search.html @@ -0,0 +1,27 @@ +
+ {:lang('条件搜索')} + +
\ No newline at end of file diff --git a/stc/database/20250621000000_install_staff_db.php b/stc/database/20250621000000_install_staff_db.php new file mode 100644 index 0000000..f2b381b --- /dev/null +++ b/stc/database/20250621000000_install_staff_db.php @@ -0,0 +1,89 @@ +_create_staff_dept(); + $this->_create_staff_user(); + } + + /** + * 创建数据对象 + * @class StaffDept + * @table staff_dept + * @return void + */ + private function _create_staff_dept() + { + // 创建数据表对象 + $table = $this->table('staff_dept', [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '部门员工', + ]); + // 创建或更新数据表 + PhinxExtend::upgrade($table, [ + ['name', 'string', ['limit' => 255, 'default' => NULL, 'null' => false, 'comment' => '部门名称']], + ['pid', 'integer', ['limit' => 11, 'default' => 0, 'null' => false, 'comment' => '上级部门ID']], + ['headman_id', 'integer', ['default' => NULL, 'null' => true, 'comment' => '领导人ID']], + ['sort', 'integer', ['limit' => 9, 'default' => 100, 'null' => false, 'comment' => '排序权重']], + ['status', 'integer', ['limit' => 11, 'default' => 1, 'null' => false, 'comment' => '状态(0禁用,1启用)']], + ['is_deleted', 'integer', ['limit' => 11, 'default' => 0, 'null' => false, 'comment' => '删除(1删除,0未删)']], + ['create_at', 'timestamp', ['default' => 'CURRENT_TIMESTAMP', 'null' => true, 'comment' => '创建时间']], + ], [ + + ], false); + } + + /** + * 创建数据对象 + * @class StaffUser + * @table staff_user + * @return void + */ + private function _create_staff_user() + { + // 创建数据表对象 + $table = $this->table('staff_user', [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '部门员工', + ]); + // 创建或更新数据表 + PhinxExtend::upgrade($table, [ + ['name', 'string', ['limit' => 255, 'default' => NULL, 'null' => false, 'comment' => '员工名称']], + ['gender', 'tinyinteger', ['limit' => 4, 'default' => 0, 'null' => true, 'comment' => '性别(0男,1女)']], + ['phone', 'string', ['limit' => 255, 'default' => NULL, 'null' => false, 'comment' => '手机号']], + ['email', 'string', ['limit' => 255, 'default' => NULL, 'null' => true, 'comment' => '电子邮箱']], + ['dept_id', 'integer', ['limit' => 11, 'default' => 0, 'null' => true, 'comment' => '所属部门']], + ['status', 'integer', ['default' => '1', 'null' => false, 'comment' => '状态(0禁用,1启用)']], + ['is_deleted', 'integer', ['default' => '0', 'null' => false, 'comment' => '删除(1删除,0未删)']], + ['create_at', 'timestamp', ['default' => 'CURRENT_TIMESTAMP', 'null' => true, 'comment' => '创建时间']], + ], [ + + ], false); + } + +} \ No newline at end of file diff --git a/stc/database/20250621000000_install_staff_menu.php b/stc/database/20250621000000_install_staff_menu.php new file mode 100644 index 0000000..b14ccbe --- /dev/null +++ b/stc/database/20250621000000_install_staff_menu.php @@ -0,0 +1,54 @@ +insertMenu(); + } + + /** + * 初始化系统菜单 + * @return void + * @throws \Exception + */ + private function insertMenu() + { + + // 初始化菜单数据 + PhinxExtend::write2menu([ + [ + 'name' => '组织架构', + 'sort' => '101', + 'subs' => Service::menu(), + ], + ], [ + 'url|node' => 'staff/user/index' + ]); + } +} \ No newline at end of file