first commit

This commit is contained in:
2026-07-23 09:40:36 +08:00
commit 090abf48fa
989 changed files with 145728 additions and 0 deletions
+170
View File
@@ -0,0 +1,170 @@
<?php
/**
+-----------------------------------------------------------------------------------------------
* GouGuOPEN [ 左手研发,右手开源,未来可期!]
+-----------------------------------------------------------------------------------------------
* @Copyright (c) 2021~2024 http://www.gouguoa.com All rights reserved.
+-----------------------------------------------------------------------------------------------
* @Licensed 勾股OA,开源且可免费使用,但并不是自由软件,未经授权许可不能去除勾股OA的相关版权信息
+-----------------------------------------------------------------------------------------------
* @Author 勾股工作室 <hdm58@qq.com>
+-----------------------------------------------------------------------------------------------
*/
use think\facade\Db;
//获取项目状态
function get_status($id=0)
{
$status_array = ['未设置','未开始','进行中','已完成','已关闭'];
if($id==0){
return $status_array;
}
else{
$news_array=[];
foreach($status_array as $key => $value){
if($key>0){
$news_array[]=array(
'id'=>$key,
'title'=>$value,
);
}
}
return $news_array;
}
}
//根据状态读取审批状态名称
function status_name($status=0)
{
$status_array = get_status();
return $status_array[$status];
}
//获取任务全部状态
function get_task_status($id=0)
{
$status_task_array = ['未设置','未开始','进行中','已完成','已拒绝','已关闭'];
if($id==0){
return $status_task_array;
}
else{
$news_array=[];
foreach($status_task_array as $key => $value){
if($key>0){
$news_array[]=array(
'id'=>$key,
'title'=>$value,
);
}
}
return $news_array;
}
}
//根据任务状态读取审批状态名称
function status_task_name($status=0)
{
$status_task_array = get_task_status();
return $status_task_array[$status];
}
//获取任务紧急程度
function get_priority($id=0)
{
$priority_array = ['未设置','低','中','高','紧急'];
if($id==0){
return $priority_array;
}
else{
$news_array=[];
foreach($priority_array as $key => $value){
if($key>0){
$news_array[]=array(
'id'=>$key,
'title'=>$value,
);
}
}
return $news_array;
}
}
//根据任务紧急程度名称
function priority_name($priority=0)
{
$priority_array = get_priority();
return $priority_array[$priority];
}
//是否是项目管理员,count>1即有权限
function isAuthProject($uid)
{
if($uid == 1){
return 1;
}
$map = [];
$map[] = ['name', '=', 'project_admin'];
$map[] = ['', 'exp', Db::raw("FIND_IN_SET('{$uid}',conf_1)")];
$count = Db::name('DataAuth')->where($map)->count();
return $count;
}
//读取项目
function get_project($uid = 0)
{
$map = [];
$map[] = ['delete_time', '=', 0];
if ($uid > 0) {
$project_ids = Db::name('ProjectUser')->where(['uid' => $uid, 'delete_time' => 0])->column('project_id');
$map[] = ['id', 'in', $project_ids];
}
$project = Db::name('Project')->where($map)->select()->toArray();
return $project;
}
//任务分配情况统计
function plan_count($arrData)
{
$documents = array();
foreach ($arrData as $index => $value) {
$planTime = date("Y-m-d", $value['end_time']);
if (empty($documents[$planTime])) {
$documents[$planTime] = 1;
} else {
$documents[$planTime] += 1;
}
}
return $documents;
}
//工时登记情况统计
function hour_count($arrData)
{
$documents = array();
foreach ($arrData as $index => $value) {
$hourTime = date("Y-m-d", $value['start_time']);
if (empty($documents[$hourTime])) {
$documents[$hourTime] = $value['labor_time'] + 0;
} else {
$documents[$hourTime] += $value['labor_time'];
}
$documents[$hourTime] = round($documents[$hourTime], 2);
}
return $documents;
}
//燃尽图统计
function cross_count($arrData)
{
$documents = array();
foreach ($arrData as $index => $value) {
$planTime = date("Y-m-d", $value['end_time']);
if (empty($documents[$planTime])) {
$documents[$planTime] = 1;
} else {
$documents[$planTime] += 1;
}
}
return $documents;
}
+623
View File
@@ -0,0 +1,623 @@
<?php
/**
+-----------------------------------------------------------------------------------------------
* GouGuOPEN [ 左手研发,右手开源,未来可期!]
+-----------------------------------------------------------------------------------------------
* @Copyright (c) 2021~2024 http://www.gouguoa.com All rights reserved.
+-----------------------------------------------------------------------------------------------
* @Licensed 勾股OA,开源且可免费使用,但并不是自由软件,未经授权许可不能去除勾股OA的相关版权信息
+-----------------------------------------------------------------------------------------------
* @Author 勾股工作室 <hdm58@qq.com>
+-----------------------------------------------------------------------------------------------
*/
declare (strict_types = 1);
namespace app\project\controller;
use app\api\BaseController;
use app\project\model\Project as ProjectModel;
use app\project\model\ProjectTask;
use app\oa\model\Schedule;
use think\facade\Db;
use think\facade\View;
class Api extends BaseController
{
/**
* 构造函数
*/
protected $model;
public function __construct()
{
parent::__construct(); // 调用父类构造函数
$this->model = new ProjectModel();
}
//获取项目列表
public function get_project()
{
$param = get_params();
$uid = $this->uid;
$auth = isAuth($uid,'project_admin','conf_1');
$where = array();
$whereOr = array();
$where[] = ['delete_time', '=', 0];
if($auth == 0){
$whereOr[] = ['director_uid', '=', $uid];
$project_ids = Db::name('ProjectUser')->where(['uid' => $uid, 'delete_time' => 0])->column('project_id');
$whereOr[] = ['id', 'in', $project_ids];
$dids_a = get_leader_departments($uid);
$dids_b = get_role_departments($uid);
$dids = array_merge($dids_a, $dids_b);
if(!empty($dids)){
$whereOr[] = ['did','in',$dids];
}
}
if (!empty($param['keywords'])) {
$where[] = ['name|content', 'like', '%' . $param['keywords'] . '%'];
}
$list = $this->model->datalist($param,$where,$whereOr);
return table_assign(0, '', $list);
}
//获取任务列表
public function get_task()
{
$param = get_params();
$uid = $this->uid;
$where = [];
$whereOr = [];
$where[] = ['delete_time', '=', 0];
if (!empty($param['status'])) {
$where[] = ['status', '=', $param['status']];
}
if (!empty($param['priority'])) {
$where[] = ['priority', '=', $param['priority']];
}
if (!empty($param['work_id'])) {
$where[] = ['work_id', '=', $param['work_id']];
}
if (!empty($param['director_uid'])) {
$where[] = ['director_uid', 'in', $param['director_uid']];
}
if (!empty($param['keywords'])) {
$where[] = ['title|content', 'like', '%' . $param['keywords'] . '%'];
}
if(!empty($param['project_id'])){
$where[] = ['project_id', '=', $param['project_id']];
}
else{
$auth = isAuth($uid,'project_admin','conf_1');
if($auth == 0){
$whereOr[] = ['admin_id', '=', $uid];
$whereOr[] = ['', 'exp', Db::raw("FIND_IN_SET('{$uid}',assist_admin_ids)")];
$dids_a = get_leader_departments($uid);
$dids_b = get_role_departments($uid);
$dids = array_merge($dids_a, $dids_b);
if(!empty($dids)){
$whereOr[] = ['did','in',$dids];
}
if (empty($param['director_uid'])) {
$whereOr[] = ['director_uid', '=', $uid];
}
}
}
$model = new ProjectTask();
$list = $model->datalist($param,$where,$whereOr);
return table_assign(0, '', $list);
}
//获取项目概况数据
public function get_chart_data()
{
$param = get_params();
$tasks = Db::name('ProjectTask')->field('id,plan_hours,end_time,status,over_time')->order('end_time asc')->where([['project_id', '=', $param['project_id']],['end_time','>',0],['delete_time', '=', 0]])->select()->toArray();
$task_count = count($tasks);
$task_count_ok = Db::name('ProjectTask')->where([['project_id', '=', $param['project_id']], ['delete_time', '=', 0],['status', '>', 2]])->count();
$task_delay = 0;
if ($task_count > 0) {
foreach ($tasks as $k => $v) {
if (($v['status'] < 3) && ($v['end_time'] < time() - 86400)) {
$task_delay++;
}
if (($v['status'] == 3) && ($v['end_time'] < $v['over_time'] - 86400)) {
$task_delay++;
}
}
}
$task_pie = [
'count' => $task_count,
'count_ok' => $task_count_ok,
'delay' => $task_delay,
'ok_lv' => $task_count == 0 ? 100 : round($task_count_ok * 100 / $task_count, 2),
'delay_lv' => $task_count == 0 ? 100 : round($task_delay * 100 / $task_count, 2),
];
$date_tasks = [];
if ($tasks) {
$date_tasks = plan_count($tasks);
}
$tasks_ok = Db::name('ProjectTask')->field('id,over_time as end_time')->order('over_time asc')->where([['over_time', '>', 0], ['delete_time', '=', 0], ['project_id', '=', $param['project_id']]])->select()->toArray();
$date_tasks_ok = [];
if ($tasks_ok) {
$date_tasks_ok = plan_count($tasks_ok);
}
$tids = Db::name('ProjectTask')->where(['delete_time' => 0, 'project_id' => $param['project_id']])->column('id');
$schedules = Db::name('Schedule')->where([['tid', 'in', $tids], ['delete_time', '=', 0]])->select()->toArray();
$date_schedules = [];
if ($schedules) {
$date_schedules = hour_count($schedules);
}
$res['task_pie'] = $task_pie;
$res['date_tasks'] = $date_tasks;
$res['date_tasks_ok'] = $date_tasks_ok;
$res['date_schedules'] = $date_schedules;
to_assign(0, '', $res);
}
//添加附件
public function add_file()
{
$param = get_params();
$param['create_time'] = time();
$param['admin_id'] = $this->uid;
$fid = Db::name('ProjectFile')->strict(false)->field(true)->insertGetId($param);
if ($fid) {
return to_assign(0, '上传成功', $fid);
}
}
//删除
public function delete_file()
{
if (request()->isDelete()) {
$id = get_params("id");
$detail = Db::name('ProjectFile')->where('id', $id)->find();
if (Db::name('ProjectFile')->where('id', $id)->delete() !== false) {
$file_name = Db::name('File')->where('id', $detail['file_id'])->value('name');
return to_assign(0, "删除成功");
} else {
return to_assign(0, "删除失败");
}
} else {
return to_assign(1, "错误的请求");
}
}
//工作记录列表
public function schedule()
{
$param = get_params();
$task_ids = Db::name('ProjectTask')->where(['delete_time' => 0, 'project_id' => $param['tid']])->column('id');
$where = array();
if (!empty($param['keywords'])) {
$where[] = ['a.title', 'like', '%' . $param['keywords'] . '%'];
}
if (!empty($param['uid'])) {
$where[] = ['a.admin_id', '=', $param['uid']];
}
if (!empty($task_ids)) {
$where[] = ['a.tid', 'in', $task_ids];
}
$where[] = ['a.delete_time', '=', 0];
$rows = empty($param['limit']) ? get_config('app.page_size') : $param['limit'];
$list = Schedule::where($where)
->field('a.*,u.name,d.title as department,t.title as task,p.name as project,w.title as work_cate')
->alias('a')
->join('Admin u', 'a.admin_id = u.id', 'LEFT')
->join('Department d', 'u.did = d.id', 'LEFT')
->join('ProjectTask t', 'a.tid = t.id', 'LEFT')
->join('WorkCate w', 'w.id = t.cate', 'LEFT')
->join('Project p', 't.project_id = p.id', 'LEFT')
->order('a.end_time desc')
->paginate(['list_rows'=> $rows])
->each(function ($item, $key) {
$item->start_time_a = empty($item->start_time) ? '' : date('Y-m-d', $item->start_time);
$item->start_time_b = empty($item->start_time) ? '' : date('H:i', $item->start_time);
$item->end_time_a = empty($item->end_time) ? '' : date('Y-m-d', $item->end_time);
$item->end_time_b = empty($item->end_time) ? '' : date('H:i', $item->end_time);
$item->start_time = empty($item->start_time) ? '' : date('Y-m-d H:i', $item->start_time);
$item->end_time = empty($item->end_time) ? '' : date('H:i', $item->end_time);
});
return table_assign(0, '', $list);
}
//查看工作记录详情
public function schedule_detail($id)
{
$id = get_params('id');
$schedule = Schedule::where(['id' => $id])->find();
if (!empty($schedule)) {
$schedule['start_time_1'] = date('H:i', $schedule['start_time']);
$schedule['end_time_1'] = date('H:i', $schedule['end_time']);
$schedule['start_time'] = date('Y-m-d', $schedule['start_time']);
$schedule['end_time'] = date('Y-m-d', $schedule['end_time']);
$schedule['user'] = Db::name('Admin')->where(['id' => $schedule['admin_id']])->value('name');
$schedule['department'] = Db::name('Department')->where(['id' => $schedule['did']])->value('title');
}
if (request()->isAjax()) {
return to_assign(0, "", $schedule);
} else {
return $schedule;
}
}
//任务的工作记录列表
public function task_schedule()
{
$param = get_params();
$where = array();
$where['a.tid'] = $param['tid'];
$where['a.delete_time'] = 0;
$list = Db::name('Schedule')
->field('a.*,u.name')
->alias('a')
->join('Admin u', 'u.id = a.admin_id')
->order('a.create_time desc')
->where($where)
->select()->toArray();
foreach ($list as $k => $v) {
$list[$k]['start_time'] = empty($v['start_time']) ? '' : date('Y-m-d H:i', $v['start_time']);
$list[$k]['end_time'] = empty($v['end_time']) ? '' : date('H:i', $v['end_time']);
}
return to_assign(0, '', $list);
}
public function project_user()
{
$param = get_params();
$project = Db::name('Project')->where(['id' => $param['tid']])->find();
$users = Db::name('ProjectUser')
->field('pu.*,a.name,a.mobile,p.title as position,d.title as department')
->alias('pu')
->join('Admin a', 'pu.uid = a.id', 'LEFT')
->join('Department d', 'a.did = d.id', 'LEFT')
->join('Position p', 'a.position_id = p.id', 'LEFT')
->order('pu.id asc')
->where(['pu.project_id' => $param['tid']])
->select()->toArray();
if(!empty($users)){
foreach ($users as $k => &$v) {
$v['role'] = 0; //普通项目成员
if ($v['uid'] == $project['admin_id']) {
$v['role'] = 1; //项目创建人
}
if ($v['uid'] == $project['director_uid']) {
$v['role'] = 2; //项目负责人
}
$v['create_time'] = date('Y-m-d', (int) $v['create_time']);
if($v['delete_time'] > 0){
$v['delete_time'] = date('Y-m-d', (int) $v['delete_time']);
}
else{
$v['delete_time'] = '-';
}
$tids = Db::name('ProjectTask')->where([['project_id','=',$param['tid']],['delete_time','=',0]])->column('id');
$schedule_map = [];
$schedule_map[] = ['tid','in',$tids];
$schedule_map[] = ['delete_time','=',0];
$schedule_map[] = ['admin_id','=',$v['uid']];
$v['schedules'] = Db::name('Schedule')->where($schedule_map)->count();
$v['labor_times'] = Db::name('Schedule')->where($schedule_map)->sum('labor_time');
$task_map = [];
$task_map[] = ['project_id','=',$param['tid']];
$task_map[] = ['delete_time', '=', 0];
$task_map1 = [
['admin_id', '=', $v['uid']],
];
$task_map2 = [
['director_uid', '=', $v['uid']],
];
$task_map3 = [
['', 'exp', Db::raw("FIND_IN_SET('{$v['uid']}',assist_admin_ids)")],
];
//任务总数
$v['tasks_total'] = Db::name('ProjectTask')
->where(function ($query) use ($task_map1, $task_map2, $task_map3) {
$query->where($task_map1)->whereor($task_map2)->whereor($task_map3);
})
->where($task_map)->count();
//已完成任务
$task_map[] = ['status', '>', 2]; //已完成
$v['tasks_finish'] = Db::name('ProjectTask')->where(function ($query) use ($task_map1, $task_map2, $task_map3) {
$query->where($task_map1)->whereor($task_map2)->whereor($task_map3);
})
->where($task_map)->count();
//未完成任务
$v['tasks_unfinish'] = $v['tasks_total'] - $v['tasks_finish'];
$v['tasks_pensent'] = "100";
if ($v['tasks_total'] > 0) {
$v['tasks_pensent'] = round($v['tasks_finish'] / $v['tasks_total'] * 100, 2) . "";
}
}
}
to_assign(0, '', $users);
}
//新增项目成员
public function add_user()
{
$param = get_params();
if (request()->isPost()) {
if($param['uid'] == 0){
to_assign(1, '已取消');
}
$has = Db::name('ProjectUser')->where(['uid' => $param['uid'],'project_id'=>$param['project_id']])->find();
if(!empty($has)){
to_assign(1, '该员工已经是项目成员');
}
$project = Db::name('Project')->where(['id' => $param['project_id']])->find();
if($this->uid == 1 || $this->uid == $project['admin_id'] || $this->uid == $project['director_uid']){
$param['admin_id'] = $this->uid;
$param['create_time'] = time();
$res = Db::name('ProjectUser')->strict(false)->field(true)->insert($param);
if ($res) {
to_assign();
}
}else{
to_assign(1, '只有项目创建者和负责人才有权限新增项目成员');
}
}
}
//移除项目成员
public function remove_user()
{
$param = get_params();
if (request()->isDelete()) {
$detail = Db::name('ProjectUser')->where(['id' => $param['id']])->find();
$project = Db::name('Project')->where(['id' => $detail['project_id']])->find();
if($this->uid == $project['admin_id'] || $this->uid == $project['director_uid']){
if($detail['uid'] == $project['admin_id']){
to_assign(1, '该项目成员是项目的创建者,不能移除');
}
if($detail['uid'] == $project['director_uid']){
to_assign(1, '该项目成员是项目的负责人,需要去除负责人权限才能移除');
}
$param['delete_time'] = time();
if (Db::name('ProjectUser')->update($param) !== false) {
return to_assign(0, "移除成功");
} else {
return to_assign(1, "移除失败");
}
}else{
to_assign(1, '只有项目创建者和负责人才有权限移除项目成员');
}
}else{
return to_assign(1, "错误的请求");
}
}
//恢复项目成员
public function recover_user()
{
$param = get_params();
if (request()->isPost()) {
$detail = Db::name('ProjectUser')->where(['id' => $param['id']])->find();
$project = Db::name('Project')->where(['id' => $detail['project_id']])->find();
if($this->uid == $project['admin_id'] || $this->uid == $project['director_uid']){
$param['delete_time'] = 0;
if (Db::name('ProjectUser')->update($param) !== false) {
return to_assign(0, "恢复成功");
} else {
return to_assign(1, "恢复失败");
}
}else{
to_assign(1, '只有项目创建者和负责人才有权限恢复项目成员');
}
}else{
return to_assign(1, "错误的请求");
}
}
//关闭项目
public function close()
{
if (request()->isPost()) {
$id = get_params("id");
$project = Db::name('Project')->where('id', $id)->find();
if($project['status'] == 3){
return to_assign(1, "已完成的项目当不能关闭");
}
$auth = isAuth($this->uid,'project_admin','conf_1');
if($project['admin_id'] == $this->uid || $project['director_uid'] == $this->uid || $auth==1){
if (Db::name('Project')->where('id', $id)->update(['status'=>4]) !== false) {
add_log('close', $id, [],'项目');
return to_assign(0, "操作成功");
} else {
return to_assign(1, "操作失败");
}
}
else{
return to_assign(1, "只有项目管理员、项目创建人、项目负责人才有权限关闭");
}
} else {
return to_assign(1, "错误的请求");
}
}
//开启项目
public function open()
{
if (request()->isPost()) {
$id = get_params("id");
$project = Db::name('Project')->where('id', $id)->find();
if($project['status'] == 3){
return to_assign(1, "已完成的项目当不能开启");
}
$auth = isAuth($this->uid,'project_admin','conf_1');
if($project['admin_id'] == $this->uid || $project['director_uid'] == $this->uid || $auth==1){
if (Db::name('Project')->where('id', $id)->update(['status'=>2]) !== false) {
add_log('open', $id, [],'项目');
return to_assign(0, "操作成功");
} else {
return to_assign(1, "操作失败");
}
}
else{
return to_assign(1, "只有项目管理员、项目创建人、项目负责人才有权限开启");
}
} else {
return to_assign(1, "错误的请求");
}
}
//删除已有的项目步骤
public function step_del()
{
if (request()->isDelete()) {
$id = get_params("id");
$step = Db::name('ProjectStep')->where('id', $id)->find();
if($step['is_current'] == 1){
return to_assign(1, "项目当前所在步骤不能删除");
}
$project = Db::name('Project')->where('id', $step['project_id'])->find();
$auth = isAuth($this->uid,'project_admin','conf_1');
if($project['admin_id'] == $this->uid || $project['director_uid'] == $this->uid || $auth==1){
if (Db::name('ProjectStep')->where('id', $id)->update(['delete_time'=>time()]) !== false) {
return to_assign(0, "删除成功");
} else {
return to_assign(1, "删除失败");
}
}
else{
return to_assign(1, "只有项目管理员、项目创建人、项目负责人才有权限删除");
}
} else {
return to_assign(1, "错误的请求");
}
}
//审核
public function step_check()
{
$param = get_params();
$detail = Db::name('Project')->where(['id' => $param['id']])->find();
if($detail['status'] > 3){
return to_assign(1, "不支持该操作:项目已关闭");
}
//当前审核节点详情
$step = Db::name('ProjectStep')->where(['project_id'=>$detail['id'],'is_current' => 1,'delete_time'=>0])->find();
if (!empty($step) && $this->uid != $step['director_uid']){
return to_assign(1,'您没权限操作');
}
//审核通过
if($param['check'] == 1){
$next_step = Db::name('ProjectStep')->where([['project_id','=',$detail['id']],['sort','>',$step['sort']],['delete_time','=',0]])->order('sort', 'asc')->find();
if(!empty($next_step)){
Db::name('ProjectStep')->where('id', $step['id'])->strict(false)->field(true)->update(['is_current'=>0]);
Db::name('ProjectStep')->where('id', $next_step['id'])->strict(false)->field(true)->update(['is_current'=>1]);
$param['status'] = 2;
}
else{
//不存在下一步审核,审核结束
$param['status'] = 3;
Db::name('ProjectStep')->where('id', $step['id'])->strict(false)->field(true)->update(['is_current'=>0]);
}
//审核通过数据操作
Db::name('Project')->strict(false)->field('status')->update($param);
$checkData=array(
'project_id' => $detail['id'],
'step_id' => $step['id'],
'check_uid' => $this->uid,
'check_time' => time(),
'status' => $param['check'],
'content' => $param['content'],
'create_time' => time()
);
Db::name('ProjectStepRecord')->strict(false)->field(true)->insertGetId($checkData);
add_log('check', $param['id'], $param,'项目阶段');
return to_assign();
}
//回退审核
else if($param['check'] == 2){
//获取上一步的审核信息
$prev_step = Db::name('ProjectStep')->where([['project_id','=',$detail['id']],['sort','<',$step['sort']],['delete_time','=',0]])->order('sort', 'desc')->find();
if(!empty($prev_step)){
//存在上一步审核
Db::name('ProjectStep')->where('id', $step['id'])->strict(false)->field(true)->update(['is_current'=>0]);
Db::name('ProjectStep')->where('id', $prev_step['id'])->strict(false)->field(true)->update(['is_current'=>1]);
$checkData=array(
'project_id' => $detail['id'],
'step_id' => $step['id'],
'check_uid' => $this->uid,
'check_time' => time(),
'status' => $param['check'],
'content' => $param['content'],
'create_time' => time()
);
Db::name('ProjectStepRecord')->strict(false)->field(true)->insertGetId($checkData);
add_log('refue', $param['id'], $param,'项目阶段');
return to_assign();
}
else{
return to_assign(1,'已经是第一个阶段了');
}
}
//回退审核
else if($param['check'] == 3){
$auth = isAuth($this->uid,'project_admin','conf_1');
if($detail['admin_id'] == $this->uid || $detail['director_uid'] == $this->uid || $auth==1){
//获取最后一步的审核信息
$prev_step = Db::name('ProjectStep')->where([['project_id','=',$detail['id']],['delete_time','=',0]])->order('sort', 'desc')->find();
if(!empty($prev_step)){
//最后一步审核
Db::name('ProjectStep')->where('id', $prev_step['id'])->strict(false)->field(true)->update(['is_current'=>1]);
Db::name('Project')->where('id', $detail['id'])->strict(false)->field(true)->update(['status'=>2]);
$checkData=array(
'project_id' => $detail['id'],
'step_id' => $prev_step['id'],
'check_uid' => $this->uid,
'check_time' => time(),
'status' => $param['check'],
'content' => $param['content'],
'create_time' => time()
);
Db::name('ProjectStepRecord')->strict(false)->field(true)->insertGetId($checkData);
add_log('refue', $param['id'], $param,'项目阶段');
return to_assign();
}
else{
return to_assign(1,'项目阶段不存在');
}
}
else{
return to_assign(1, "只有项目管理员、项目创建人、项目负责人才有权限操作");
}
}
}
//获取项目类别
public function get_project_cate()
{
$list = get_base_data('project_cate');
return to_assign(0, '', $list);
}
public function get_project_step()
{
$param = get_params();
$where = array();
if (!empty($param['keywords'])) {
$where[] = ['title', 'like', '%' . $param['keywords'] . '%'];
}
$where[] = ['status', '=', 1];
$where[] = ['delete_time', '=', 0];
$rows = empty($param['limit']) ? get_config('app.page_size') : $param['limit'];
$list = Db::name('Step')->where($where)->order('id desc')->paginate(['list_rows'=> $rows]);
return table_assign(0, '', $list);
}
public function project_edit()
{
$param = get_params();
$this->model->apiedit($param);
}
}
+141
View File
@@ -0,0 +1,141 @@
<?php
/**
+-----------------------------------------------------------------------------------------------
* GouGuOPEN [ 左手研发,右手开源,未来可期!]
+-----------------------------------------------------------------------------------------------
* @Copyright (c) 2021~2024 http://www.gouguoa.com All rights reserved.
+-----------------------------------------------------------------------------------------------
* @Licensed 勾股OA,开源且可免费使用,但并不是自由软件,未经授权许可不能去除勾股OA的相关版权信息
+-----------------------------------------------------------------------------------------------
* @Author 勾股工作室 <hdm58@qq.com>
+-----------------------------------------------------------------------------------------------
*/
declare (strict_types = 1);
namespace app\project\controller;
use app\base\BaseController;
use app\project\model\ProjectCate as ProjectCateModel;
use app\project\validate\CateValidate;
use think\exception\ValidateException;
use think\facade\Db;
use think\facade\View;
class Cate extends BaseController
{
/**
* 构造函数
*/
protected $model;
public function __construct()
{
parent::__construct(); // 调用父类构造函数
$this->model = new ProjectCateModel();
}
/**
* 数据列表
*/
public function datalist()
{
if (request()->isAjax()) {
$list = $this->model->where('delete_time',0)->order('sort asc')->select();
return to_assign(0, '', $list);
}
else{
return view();
}
}
/**
* 添加/编辑
*/
public function add()
{
$param = get_params();
if (request()->isAjax()) {
if (!empty($param['id']) && $param['id'] > 0) {
try {
validate(CateValidate::class)->scene('edit')->check($param);
} catch (ValidateException $e) {
// 验证失败 输出错误信息
return to_assign(1, $e->getError());
}
$this->model->edit($param);
} else {
try {
validate(CateValidate::class)->scene('add')->check($param);
} catch (ValidateException $e) {
// 验证失败 输出错误信息
return to_assign(1, $e->getError());
}
$this->model->add($param);
}
}else{
$id = isset($param['id']) ? $param['id'] : 0;
if ($id>0) {
$detail = $this->model->getById($id);
View::assign('detail', $detail);
}
return view();
}
}
/**
* 查看
*/
public function view($id)
{
$detail = $this->model->getById($id);
if (!empty($detail)) {
View::assign('detail', $detail);
return view();
}
else{
return view(EEEOR_REPORTING,['code'=>404,'warning'=>'找不到页面']);
}
}
/**
* 删除
*/
public function del()
{
if (request()->isDelete()) {
$param = get_params();
$id = isset($param['id']) ? $param['id'] : 0;
$type = isset($param['type']) ? $param['type'] : 0;
$this->model->delById($id,$type);
} else {
return to_assign(1, "错误的请求");
}
}
/**
* 设置
*/
public function set()
{
if (request()->isAjax()) {
$param = get_params();
$res = $this->model->strict(false)->field('id,status')->update($param);
if ($res) {
if($param['status'] == 0){
add_log('disable', $param['id'], $param);
}
else if($param['status'] == 1){
add_log('recovery', $param['id'], $param);
}
return to_assign();
}
else{
return to_assign(0, '操作失败');
}
} else {
return to_assign(1, "错误的请求");
}
}
}
+163
View File
@@ -0,0 +1,163 @@
<?php
/**
+-----------------------------------------------------------------------------------------------
* GouGuOPEN [ 左手研发,右手开源,未来可期!]
+-----------------------------------------------------------------------------------------------
* @Copyright (c) 2021~2024 http://www.gouguoa.com All rights reserved.
+-----------------------------------------------------------------------------------------------
* @Licensed 勾股OA,开源且可免费使用,但并不是自由软件,未经授权许可不能去除勾股OA的相关版权信息
+-----------------------------------------------------------------------------------------------
* @Author 勾股工作室 <hdm58@qq.com>
+-----------------------------------------------------------------------------------------------
*/
declare (strict_types = 1);
namespace app\project\controller;
use app\base\BaseController;
use app\project\model\ProjectDocument;
use app\project\validate\DocumentCheck;
use think\exception\ValidateException;
use think\facade\Db;
use think\facade\View;
class Document extends BaseController
{
/**
* 构造函数
*/
protected $model;
public function __construct()
{
parent::__construct(); // 调用父类构造函数
$this->model = new ProjectDocument();
}
public function datalist()
{
if (request()->isAjax()) {
$param = get_params();
$where = array();
$whereOr = array();
if (!empty($param['keywords'])) {
$where[] = ['title|content', 'like', '%' . $param['keywords'] . '%'];
}
if (!empty($param['project_id'])) {
$where[] = ['project_id', '=', $param['project_id']];
} else {
$project_ids = Db::name('ProjectUser')->where(['uid' => $this->uid, 'delete_time' => 0])->column('project_id');
$whereOr[] = ['admin_id', '=', $this->uid];
$whereOr[] = ['project_id', 'in', $project_ids];
}
$where[] = ['delete_time', '=', 0];
$list = $this->model->datalist($param,$where,$whereOr);
return table_assign(0, '', $list);
} else {
return view();
}
}
//添加
public function add()
{
$param = get_params();
if (request()->isPost()) {
if (!empty($param['id']) && $param['id'] > 0) {
$detail = $this->model->detail($param['id']);
try {
validate(DocumentCheck::class)->scene('edit')->check($param);
} catch (ValidateException $e) {
// 验证失败 输出错误信息
return to_assign(1, $e->getError());
}
$param['update_time'] = time();
$res = ProjectDocument::where('id', $param['id'])->strict(false)->field(true)->update($param);
if ($res) {
add_log('edit', $param['id'], $param);
}
return to_assign();
} else {
try {
validate(DocumentCheck::class)->scene('add')->check($param);
} catch (ValidateException $e) {
// 验证失败 输出错误信息
return to_assign(1, $e->getError());
}
$param['create_time'] = time();
$param['admin_id'] = $this->uid;
$sid = ProjectDocument::strict(false)->field(true)->insertGetId($param);
if ($sid) {
add_log('add', $sid, $param);
}
return to_assign();
}
} else {
$id = isset($param['id']) ? $param['id'] : 0;
$project_id = isset($param['project_id']) ? $param['project_id'] : 0;
if($id>0){
$detail = $this->model->detail($id);
if($detail['file_ids'] !=''){
$file_array = Db::name('File')->where('id','in',$detail['file_ids'])->select();
$detail['file_array'] = $file_array;
}
View::assign('detail', $detail);
}
if($project_id>0){
$project_name = Db::name('Project')->where(['id' => $project_id])->value('name');
View::assign('project_name', $project_name);
}
View::assign('project_id', $project_id);
View::assign('id', $id);
return view();
}
}
//查看
public function view()
{
$param = get_params();
$id = isset($param['id']) ? $param['id'] : 0;
$detail = $this->model->detail($id);
if (empty($detail)) {
if (empty($detail)) {
echo '<div style="text-align:center;color:red;margin-top:20%;">该文档不存在</div>';exit;
}
} else {
$project_ids = Db::name('ProjectUser')->where(['uid' => $this->uid, 'delete_time' => 0])->column('project_id');
if (in_array($detail['project_id'], $project_ids) || ($this->uid = $detail['admin_id'])) {
if($detail['file_ids'] !=''){
$file_array = Db::name('File')->where('id','in',$detail['file_ids'])->select();
$detail['file_array'] = $file_array;
}
View::assign('detail', $detail);
if(is_mobile()){
return view('qiye@/project/document_view');
}
return view();
}
else{
echo '<div style="text-align:center;color:red;margin-top:20%;">您没权限查看该文档</div>';exit;
}
}
}
//删除
public function delete()
{
if (request()->isDelete()) {
$id = get_params("id");
$detail = Db::name('ProjectDocument')->where('id', $id)->find();
if ($detail['admin_id'] != $this->uid) {
return to_assign(1, "你不是该文档的创建人,无权限删除");
}
if (Db::name('ProjectDocument')->where('id', $id)->update(['delete_time' => time()]) !== false) {
return to_assign(0, "删除成功");
} else {
return to_assign(0, "删除失败");
}
} else {
return to_assign(1, "错误的请求");
}
}
}
+311
View File
@@ -0,0 +1,311 @@
<?php
/**
+-----------------------------------------------------------------------------------------------
* GouGuOPEN [ 左手研发,右手开源,未来可期!]
+-----------------------------------------------------------------------------------------------
* @Copyright (c) 2021~2024 http://www.gouguoa.com All rights reserved.
+-----------------------------------------------------------------------------------------------
* @Licensed 勾股OA,开源且可免费使用,但并不是自由软件,未经授权许可不能去除勾股OA的相关版权信息
+-----------------------------------------------------------------------------------------------
* @Author 勾股工作室 <hdm58@qq.com>
+-----------------------------------------------------------------------------------------------
*/
declare (strict_types = 1);
namespace app\project\controller;
use app\base\BaseController;
use app\project\model\Project as ProjectModel;
use app\project\validate\IndexValidate;
use think\exception\ValidateException;
use think\facade\Db;
use think\facade\View;
class Index extends BaseController
{
/**
* 构造函数
*/
protected $model;
public function __construct()
{
parent::__construct(); // 调用父类构造函数
$this->model = new ProjectModel();
}
/**
* 数据列表
*/
public function datalist()
{
$param = get_params();
$uid = $this->uid;
$auth = isAuth($uid,'project_admin','conf_1');
$tab = isset($param['tab']) ? $param['tab'] : 0;
if (request()->isAjax()) {
$where = array();
$whereOr = array();
$where[] = ['delete_time', '=', 0];
if (!empty($param['status'])) {
$where[] = ['status', '=', $param['status']];
}
if (!empty($param['cate_id'])) {
$where[] = ['cate_id', '=', $param['cate_id']];
}
if (!empty($param['keywords'])) {
$where[] = ['name|content', 'like', '%' . $param['keywords'] . '%'];
}
if (!empty($param['director_uid'])) {
$where[] = ['director_uid', 'in', $param['director_uid']];
}
else{
if($auth == 0){
$whereOr[] = ['director_uid', '=', $uid];
$project_ids = Db::name('ProjectUser')->where(['uid' => $uid, 'delete_time' => 0])->column('project_id');
$whereOr[] = ['id', 'in', $project_ids];
$dids_a = get_leader_departments($uid);
$dids_b = get_role_departments($uid);
$dids = array_merge($dids_a, $dids_b);
if(!empty($dids)){
$whereOr[] = ['did','in',$dids];
}
}
}
if($tab == 0){
}
if($tab == 1){
$where[] = ['status', '=', 2];
}
if($tab == 2){
$time = time();
$dalay_time = time()+7*86400;
$where[] = ['status', '<', 3];
$where[] = ['end_time', 'between', [$time,$dalay_time]];
}
if($tab == 3){
$where[] = ['status', '<', 3];
$where[] = ['end_time', '<', time()];
}
$list = $this->model->datalist($param,$where,$whereOr);
return table_assign(0, '', $list);
}
else{
View::assign('auth', $auth);
return view();
}
}
/**
* 添加/编辑
*/
public function add()
{
$param = get_params();
if (request()->isAjax()) {
if (isset($param['range_time'])) {
$range_time = explode('到',$param['range_time']);
$param['start_time'] = strtotime(urldecode(trim($range_time[0])));
$param['end_time'] = strtotime(urldecode(trim($range_time[1])));
}
else{
$param['start_time'] = strtotime(urldecode(trim($param['start_time'])));
$param['end_time'] = strtotime(urldecode(trim($param['end_time'])));
}
$step_title_data = isset($param['step_title']) ? $param['step_title'] : '';
$step_director_uid_data = isset($param['step_director_uid']) ? $param['step_director_uid'] : '';
$step_uids_data = isset($param['step_uids']) ? $param['step_uids'] : '';
$step_start_time_data = isset($param['step_start_time']) ? $param['step_start_time'] : '';
$step_end_time_data = isset($param['step_end_time']) ? $param['step_end_time'] : '';
$step_cycle_time_data = isset($param['step_cycle_time']) ? $param['step_cycle_time'] : '';
$step_remark_data = isset($param['step_remark']) ? $param['step_remark'] : '';
$step_id_data = isset($param['step_id']) ? $param['step_id'] : 0;
$step = [];
$time_1 = $param['start_time'];
$time_2 = $param['end_time'];
if(!empty($step_title_data)){
foreach ($step_title_data as $key => $value) {
if (!$value) {
continue;
}
if(!empty($step_cycle_time_data)){
$step_cycle_time = explode('到',$step_cycle_time_data[$key]);
$start_time = strtotime(urldecode(trim($step_cycle_time[0])));
$end_time = strtotime(urldecode(trim($step_cycle_time[1])));
}
else{
$start_time = strtotime(urldecode(trim($step_start_time_data[$key])));
$end_time = strtotime(urldecode(trim($step_end_time_data[$key])));
}
if($start_time<$time_1){
return to_assign(1, '第'.($key+1).'阶段的开始时间不能小于项目计划周期的开始时间');
break;
}
if($end_time>$time_2){
return to_assign(1, '第'.($key+1).'阶段的结束时间不能大于项目计划周期的结束时间');
break;
}
$item = [];
$item['title'] = $value;
$item['director_uid'] = $step_director_uid_data[$key];
$item['uids'] = $step_uids_data[$key];
$item['sort'] = $key;
$item['start_time'] = $start_time;
$item['end_time'] = $end_time;
$item['remark'] = $step_remark_data[$key];
$item['id'] = $step_id_data[$key];
$item['create_time'] = time();
$step[]=$item;
}
}
if (!empty($param['id']) && $param['id'] > 0) {
try {
validate(IndexValidate::class)->scene('edit')->check($param);
} catch (ValidateException $e) {
// 验证失败 输出错误信息
return to_assign(1, $e->getError());
}
$this->model->edit($param,$step);
} else {
try {
validate(IndexValidate::class)->scene('add')->check($param);
} catch (ValidateException $e) {
// 验证失败 输出错误信息
return to_assign(1, $e->getError());
}
$param['status'] = 2;
$param['step_sort'] = 0;
$param['admin_id'] = $this->uid;
$this->model->add($param,$step);
}
}else{
$id = isset($param['id']) ? $param['id'] : 0;
if ($id>0) {
$detail = $this->model->getById($id);
$detail['current_step'] = Db::name('ProjectStep')->where(['project_id' => $id, 'is_current' => 1,'delete_time'=>0])->value('sort');
View::assign('detail', $detail);
if(is_mobile()){
return view('qiye@/project/project_edit');
}
return view('edit');
}
if(is_mobile()){
return view('qiye@/project/project_add');
}
return view();
}
}
/**
* 查看
*/
public function view($id)
{
$detail = $this->model->getById($id);
if (!empty($detail)) {
$detail['status_name'] = status_name($detail['status']);
$detail['cate'] = Db::name('ProjectCate')->where([['id', '=', $detail['cate_id']]])->value('title');
$team_admin_ids = Db::name('ProjectUser')->where(['delete_time' => 0,'project_id'=>$id])->column('uid');
$team_admin_names = Db::name('Admin')->where('id', 'in', $team_admin_ids)->column('name');
$detail['team_admin_names'] = implode(',', $team_admin_names);
$tids = Db::name('ProjectTask')->where([['project_id', '=', $detail['id']], ['delete_time', '=', 0]])->column('id');
$detail['schedules'] = Db::name('Schedule')->where([['tid', 'in', $tids], ['delete_time', '=', 0]])->count();
$detail['hours'] = Db::name('Schedule')->where([['tid', 'in', $tids], ['delete_time', '=', 0]])->sum('labor_time');
$detail['plan_hours'] = Db::name('ProjectTask')->where([['project_id', '=', $detail['id']], ['delete_time', '=', 0]])->sum('plan_hours');
$detail['tasks'] = Db::name('ProjectTask')->where([['project_id', '=', $detail['id']],['delete_time', '=', 0]])->count();
$detail['tasks_finish'] = Db::name('ProjectTask')->where([['project_id', '=', $detail['id']],['status', '>', 2], ['delete_time', '=', 0]])->count();
$detail['tasks_unfinish'] = $detail['tasks'] - $detail['tasks_finish'];
$task_map = [];
$task_map[] = ['project_id', '=', $detail['id']];
$task_map[] = ['delete_time', '=', 0];
//判断是否是创建者或者负责人
$role = 0;
if ($detail['admin_id'] == $this->uid) {
$role = 1; //创建人
}
if ($detail['director_uid'] == $this->uid) {
$role = 2; //负责人
}
$auth = isAuth($this->uid,'project_admin','conf_1');
if ($auth == 1) {
$role = 3; //项目管理员
}
//相关附件
$file_array = Db::name('ProjectFile')
->field('mf.id,mf.topic_id,mf.admin_id,f.id as file_id,f.name,f.filesize,f.filepath,f.fileext,f.create_time,f.admin_id,a.name as admin_name')
->alias('mf')
->join('File f', 'mf.file_id = f.id', 'LEFT')
->join('Admin a', 'mf.admin_id = a.id', 'LEFT')
->order('mf.create_time desc')
->where(array('mf.topic_id' => $id, 'mf.module' => 'project'))
->select()->toArray();
//阶段操作记录
$step_record = Db::name('ProjectStepRecord')
->field('s.*,a.name as check_name,p.title')
->alias('s')
->join('Admin a', 'a.id = s.check_uid', 'LEFT')
->join('ProjectStep p', 'p.id = s.step_id', 'LEFT')
->order('s.check_time asc')
->where(array('s.project_id' => $id))
->select()->toArray();
foreach ($step_record as $kk => &$vv) {
$vv['check_time_str'] = date('Y-m-d H:i', $vv['check_time']);
$vv['status_str'] = '提交';
if($vv['status'] == 1){
$vv['status_str'] = '确认完成';
}
else if($vv['status'] == 2){
$vv['status_str'] = '回退';
}
if($vv['status'] == 3){
$vv['status_str'] = '撤销';
}
if($vv['content'] == ''){
$vv['content'] = '无';
}
}
//当前项目阶段
$step = Db::name('ProjectStep')->where(array('project_id' => $id, 'is_current' => 1,'delete_time'=>0))->find();
if(!empty($step)){
$step['director_name'] = Db::name('Admin')->where(['id' => $step['director_uid']])->value('name');
$unames = Db::name('Admin')->where([['id','in',$step['uids']]])->column('name');
$step['unames'] = implode(',',$unames);
}
View::assign('role', $role);
View::assign('file_array', $file_array);
View::assign('step', $step);
View::assign('step_record', $step_record);
View::assign('detail', $detail);
if(is_mobile()){
return view('qiye@/project/view');
}
return view();
}
else{
return view(EEEOR_REPORTING,['code'=>404,'warning'=>'找不到页面']);
}
}
/**
* 删除
*/
public function del()
{
$param = get_params();
$id = isset($param['id']) ? $param['id'] : 0;
if (request()->isDelete()) {
$this->model->delById($id);
} else {
return to_assign(1, "错误的请求");
}
}
}
+141
View File
@@ -0,0 +1,141 @@
<?php
/**
+-----------------------------------------------------------------------------------------------
* GouGuOPEN [ 左手研发,右手开源,未来可期!]
+-----------------------------------------------------------------------------------------------
* @Copyright (c) 2021~2024 http://www.gouguoa.com All rights reserved.
+-----------------------------------------------------------------------------------------------
* @Licensed 勾股OA,开源且可免费使用,但并不是自由软件,未经授权许可不能去除勾股OA的相关版权信息
+-----------------------------------------------------------------------------------------------
* @Author 勾股工作室 <hdm58@qq.com>
+-----------------------------------------------------------------------------------------------
*/
declare (strict_types = 1);
namespace app\project\controller;
use app\base\BaseController;
use app\project\model\Step as StepModel;
use app\project\validate\StepValidate;
use think\exception\ValidateException;
use think\facade\Db;
use think\facade\View;
class Step extends BaseController
{
/**
* 构造函数
*/
protected $model;
public function __construct()
{
parent::__construct(); // 调用父类构造函数
$this->model = new StepModel();
}
/**
* 数据列表
*/
public function datalist()
{
if (request()->isAjax()) {
$list = $this->model->where('delete_time',0)->order('sort asc')->select();
return to_assign(0, '', $list);
}
else{
return view();
}
}
/**
* 添加/编辑
*/
public function add()
{
$param = get_params();
if (request()->isAjax()) {
if (!empty($param['id']) && $param['id'] > 0) {
try {
validate(StepValidate::class)->scene('edit')->check($param);
} catch (ValidateException $e) {
// 验证失败 输出错误信息
return to_assign(1, $e->getError());
}
$this->model->edit($param);
} else {
try {
validate(StepValidate::class)->scene('add')->check($param);
} catch (ValidateException $e) {
// 验证失败 输出错误信息
return to_assign(1, $e->getError());
}
$this->model->add($param);
}
}else{
$id = isset($param['id']) ? $param['id'] : 0;
if ($id>0) {
$detail = $this->model->getById($id);
View::assign('detail', $detail);
}
return view();
}
}
/**
* 查看
*/
public function view($id)
{
$detail = $this->model->getById($id);
if (!empty($detail)) {
View::assign('detail', $detail);
return view();
}
else{
return view(EEEOR_REPORTING,['code'=>404,'warning'=>'找不到页面']);
}
}
/**
* 删除
*/
public function del()
{
if (request()->isDelete()) {
$param = get_params();
$id = isset($param['id']) ? $param['id'] : 0;
$type = isset($param['type']) ? $param['type'] : 0;
$this->model->delById($id,$type);
} else {
return to_assign(1, "错误的请求");
}
}
/**
* 设置
*/
public function set()
{
if (request()->isAjax()) {
$param = get_params();
$res = $this->model->strict(false)->field('id,status')->update($param);
if ($res) {
if($param['status'] == 0){
add_log('disable', $param['id'], $param);
}
else if($param['status'] == 1){
add_log('recovery', $param['id'], $param);
}
return to_assign();
}
else{
return to_assign(0, '操作失败');
}
} else {
return to_assign(1, "错误的请求");
}
}
}
+319
View File
@@ -0,0 +1,319 @@
<?php
/**
+-----------------------------------------------------------------------------------------------
* GouGuOPEN [ 左手研发,右手开源,未来可期!]
+-----------------------------------------------------------------------------------------------
* @Copyright (c) 2021~2024 http://www.gouguoa.com All rights reserved.
+-----------------------------------------------------------------------------------------------
* @Licensed 勾股OA,开源且可免费使用,但并不是自由软件,未经授权许可不能去除勾股OA的相关版权信息
+-----------------------------------------------------------------------------------------------
* @Author 勾股工作室 <hdm58@qq.com>
+-----------------------------------------------------------------------------------------------
*/
declare (strict_types = 1);
namespace app\project\controller;
use app\base\BaseController;
use app\project\model\ProjectTask;
use app\api\model\Comment;
use app\oa\model\Schedule;
use app\api\model\EditLog;
use app\project\validate\TaskCheck;
use think\exception\ValidateException;
use think\facade\Db;
use think\facade\View;
class Task extends BaseController
{
/**
* 构造函数
*/
protected $model;
public function __construct()
{
parent::__construct(); // 调用父类构造函数
$this->model = new ProjectTask();
}
/**
* 数据列表
*/
public function datalist()
{
$param = get_params();
$uid = $this->uid;
$auth = isAuth($uid,'project_admin','conf_1');
if (request()->isAjax()) {
$tab = isset($param['tab']) ? $param['tab'] : 0;
$time = time();
$time = time();
$dalay_time = time()+7*86400;
$where = [];
$whereOr = [];
$where[] = ['delete_time', '=', 0];
if (!empty($param['status'])) {
$where[] = ['status', '=', $param['status']];
}
if (!empty($param['priority'])) {
$where[] = ['priority', '=', $param['priority']];
}
if (!empty($param['work_id'])) {
$where[] = ['work_id', '=', $param['work_id']];
}
if (!empty($param['keywords'])) {
$where[] = ['title|content', 'like', '%' . $param['keywords'] . '%'];
}
if(!empty($param['project_id'])){
$where[] = ['project_id', '=', $param['project_id']];
}
if (!empty($param['director_uid'])) {
$where[] = ['director_uid', 'in', $param['director_uid']];
}
else{
if($auth == 0){
$whereOr[] = ['admin_id', '=', $uid];
$whereOr[] = ['', 'exp', Db::raw("FIND_IN_SET('{$uid}',assist_admin_ids)")];
$dids_a = get_leader_departments($uid);
$dids_b = get_role_departments($uid);
$dids = array_merge($dids_a, $dids_b);
if(!empty($dids)){
$whereOr[] = ['did','in',$dids];
}
if (empty($param['director_uid'])) {
$whereOr[] = ['director_uid', '=', $uid];
}
}
}
if($tab==1){
//进行中
$where[] = ['status', '<', 3];
}
if($tab==2){
//即将逾期
$where[] = ['status', '<', 3];
$where[] = ['end_time','between',[$time,$dalay_time]];
}
if($tab==3){
//已逾期
$where[] = ['status', '<', 3];
$where[] = ['end_time','<',$time];
}
$rows = empty($param['limit']) ? get_config('app.page_size') : $param['limit'];
$order = empty($param['order']) ? 'status asc,id desc' : $param['order'];
$list = $this->model->datalist($param,$where,$whereOr);
return table_assign(0, '', $list);
}
else{
View::assign('auth', $auth);
return view();
}
}
//添加
public function add()
{
$param = get_params();
if (request()->isPost()) {
if (isset($param['end_time'])) {
$param['end_time'] = strtotime(urldecode($param['end_time']));
}
if (isset($param['status'])) {
if ($param['status'] == 3) {
$param['over_time'] = time();
$param['done_ratio'] = 100;
} else {
$param['over_time'] = 0;
$param['done_ratio'] = 10;
}
}
if (!empty($param['id']) && $param['id'] > 0) {
$old = $this->model->detail($param['id']);
try {
validate(TaskCheck::class)->scene('edit')->check($param);
} catch (ValidateException $e) {
// 验证失败 输出错误信息
return to_assign(1, $e->getError());
}
$param['update_time'] = time();
if(!empty($param['director_uid'])){
$param['did'] = Db::name('Admin')->where(['id' => $param['director_uid']])->value('did');
}
$res = ProjectTask::where('id', $param['id'])->strict(false)->field(true)->update($param);
if ($res) {
add_log('edit', $param['id'], $param);
$log=new EditLog();
$log->edit('Task',$param['id'],$param,$old);
}
return to_assign();
} else {
try {
validate(TaskCheck::class)->scene('add')->check($param);
} catch (ValidateException $e) {
// 验证失败 输出错误信息
return to_assign(1, $e->getError());
}
$param['create_time'] = time();
$param['admin_id'] = $this->uid;
if(empty($param['director_uid'])){
$param['did'] = $this->did;
}
else{
$param['did'] = Db::name('Admin')->where(['id' => $param['director_uid']])->value('did');
}
$insertId = ProjectTask::strict(false)->field(true)->insertGetId($param);
if ($insertId) {
add_log('add', $insertId, $param);
$log=new EditLog();
$log->add('Task',$insertId);
//发消息
//event('SendMessage',$msg);
}
return to_assign();
}
} else {
if (isset($param['project_id'])) {
View::assign('project_id', $param['project_id']);
}
return view();
}
}
//查看
public function view()
{
$param = get_params();
$id = isset($param['id']) ? $param['id'] : 0;
$detail = (new ProjectTask())->detail($id);
$role_uid = [$detail['admin_id'], $detail['director_uid']];
$role_edit = 'view';
if (in_array($this->uid, $role_uid)) {
$role_edit = 'edit';
}
$project_ids = Db::name('ProjectUser')->where(['uid' => $this->uid, 'delete_time' => 0])->column('project_id');
$auth = isAuth($this->uid,'project_admin','conf_1');
if (in_array($detail['project_id'], $project_ids) || in_array($this->uid, $role_uid) || in_array($this->uid, explode(",",$detail['assist_admin_ids'])) || $auth==1) {
$file_array = Db::name('ProjectFile')
->field('mf.id,mf.topic_id,mf.admin_id,mf.file_id,f.name,f.filesize,f.filepath,f.fileext,f.create_time,f.admin_id,a.name as admin_name')
->alias('mf')
->join('File f', 'mf.file_id = f.id', 'LEFT')
->join('Admin a', 'mf.admin_id = a.id', 'LEFT')
->order('mf.create_time desc')
->where(array('mf.topic_id' => $id, 'mf.module' => 'task'))
->select()->toArray();
View::assign('detail', $detail);
View::assign('file_array', $file_array);
View::assign('role_edit', $role_edit);
View::assign('id', $id);
if(is_mobile()){
return view('qiye@/project/task_view');
}
return view();
}
else{
return view(EEEOR_REPORTING,['code'=>405,'warning'=>'无权限访问']);
}
}
//删除
public function del()
{
if (request()->isDelete()) {
$id = get_params("id");
$auth = isAuth($this->uid,'project_admin','conf_1');
$detail = Db::name('ProjectTask')->where('id', $id)->find();
if ($detail['admin_id'] != $this->uid && $auth==0) {
return to_assign(1, "你不是该任务的创建人,无权限删除");
}
$hour = Db::name('Schedule')->where(['tid' => $id,'delete_time' => 0])->count();
if($hour>0){
return to_assign(1, "已有工作日志的任务不支持删除");
}
if (Db::name('ProjectTask')->where('id', $id)->update(['delete_time' => time()]) !== false) {
return to_assign(0, "删除成功");
} else {
return to_assign(0, "删除失败");
}
} else {
return to_assign(1, "错误的请求");
}
}
public function hour() {
if (request()->isAjax()) {
$param = get_params();
$uid = $this->uid;
$auth = isAuth($uid,'project_admin','conf_1');
$tid = isset($param['tid']) ? $param['tid'] : 0;
$where = [];
$whereOr = [];
//按时间检索
if (!empty($param['range_time'])) {
$range_time =explode('至', $param['range_time']);
$where[] = ['a.start_time', 'between',[strtotime($range_time[0]),strtotime($range_time[1])]];
}
if ($tid>0) {
$task_ids = Db::name('ProjectTask')->where(['delete_time' => 0, 'project_id' => $param['tid']])->column('id');
$where[] = ['a.tid', 'in', $task_ids];
}
else{
$where[] = ['a.tid', '>', 0];
if (!empty($param['keywords'])) {
$where[] = ['a.title', 'like', '%' . trim($param['keywords']) . '%'];
}
if($auth == 0){
if (!empty($param['uid'])) {
$where[] = ['a.admin_id', '=', $param['uid']];
} else {
$whereOr[] = ['a.admin_id', '=', $uid];
$dids_a = get_leader_departments($uid);
$dids_b = get_role_departments($uid);
$dids = array_merge($dids_a, $dids_b);
if(!empty($dids)){
$whereOr[] = ['a.did','in',$dids];
}
}
}
}
$where[] = ['a.delete_time', '=', 0];
$model = new Schedule();
$list = $model->datalist($param,$where,$whereOr);
return table_assign(0, '', $list);
} else {
View::assign('is_leader', isLeader($this->uid));
View::assign('role_auth', isAuth($this->uid,'office_admin','conf_1'));
View::assign('hour_auth', valueAuth('office_admin','conf_2'));
return view();
}
}
public function comment() {
if (request()->isAjax()) {
$param = get_params();
$param['admin_id'] = $this->uid;
if (!empty($param['uid'])) {
$where[] = ['admin_id', '=', $param['uid']];
}
if (!empty($param['keywords'])) {
$where[] = ['content', 'like', '%' . trim($param['keywords']) . '%'];
}
if (!empty($param['module'])) {
$where[] = ['module', '=', $param['module']];
}
if (!empty($param['topic_id'])) {
$where['topic_id'] = $param['topic_id'];
}
$where[] = ['delete_time', '=', 0];
$model = new Comment();
$list = $model->datalist($param,$where);
return table_assign(0, '', $list);
} else {
return view();
}
}
}
+141
View File
@@ -0,0 +1,141 @@
<?php
/**
+-----------------------------------------------------------------------------------------------
* GouGuOPEN [ 左手研发,右手开源,未来可期!]
+-----------------------------------------------------------------------------------------------
* @Copyright (c) 2021~2024 http://www.gouguoa.com All rights reserved.
+-----------------------------------------------------------------------------------------------
* @Licensed 勾股OA,开源且可免费使用,但并不是自由软件,未经授权许可不能去除勾股OA的相关版权信息
+-----------------------------------------------------------------------------------------------
* @Author 勾股工作室 <hdm58@qq.com>
+-----------------------------------------------------------------------------------------------
*/
declare (strict_types = 1);
namespace app\project\controller;
use app\base\BaseController;
use app\project\model\WorkCate as WorkCateModel;
use app\project\validate\WorkValidate;
use think\exception\ValidateException;
use think\facade\Db;
use think\facade\View;
class Work extends BaseController
{
/**
* 构造函数
*/
protected $model;
public function __construct()
{
parent::__construct(); // 调用父类构造函数
$this->model = new WorkCateModel();
}
/**
* 数据列表
*/
public function datalist()
{
if (request()->isAjax()) {
$list = $this->model->order('sort asc')->select();
return to_assign(0, '', $list);
}
else{
return view();
}
}
/**
* 添加/编辑
*/
public function add()
{
$param = get_params();
if (request()->isAjax()) {
if (!empty($param['id']) && $param['id'] > 0) {
try {
validate(WorkValidate::class)->scene('edit')->check($param);
} catch (ValidateException $e) {
// 验证失败 输出错误信息
return to_assign(1, $e->getError());
}
$this->model->edit($param);
} else {
try {
validate(WorkValidate::class)->scene('add')->check($param);
} catch (ValidateException $e) {
// 验证失败 输出错误信息
return to_assign(1, $e->getError());
}
$this->model->add($param);
}
}else{
$id = isset($param['id']) ? $param['id'] : 0;
if ($id>0) {
$detail = $this->model->getById($id);
View::assign('detail', $detail);
}
return view();
}
}
/**
* 查看
*/
public function view($id)
{
$detail = $this->model->getById($id);
if (!empty($detail)) {
View::assign('detail', $detail);
return view();
}
else{
return view(EEEOR_REPORTING,['code'=>404,'warning'=>'找不到页面']);
}
}
/**
* 删除
*/
public function del()
{
if (request()->isDelete()) {
$param = get_params();
$id = isset($param['id']) ? $param['id'] : 0;
$type = isset($param['type']) ? $param['type'] : 0;
$this->model->delById($id,$type);
} else {
return to_assign(1, "错误的请求");
}
}
/**
* 设置
*/
public function set()
{
if (request()->isAjax()) {
$param = get_params();
$res = $this->model->strict(false)->field('id,status')->update($param);
if ($res) {
if($param['status'] == 0){
add_log('disable', $param['id'], $param);
}
else if($param['status'] == 1){
add_log('recovery', $param['id'], $param);
}
return to_assign();
}
else{
return to_assign(0, '操作失败');
}
} else {
return to_assign(1, "错误的请求");
}
}
}
+8
View File
@@ -0,0 +1,8 @@
<?php
return [
'listen' => [
//注册监听类
'SendMessage' => ['app\listener\SendMessage'],
],
//'subscribe' => ['app\subscribe\Message'],
];
+9
View File
@@ -0,0 +1,9 @@
<?php
// 这是系统自动生成的middleware定义文件
return [
//开启session中间件
//'think\middleware\SessionInit',
//验证勾股OA是否完成安装
\app\home\middleware\Install::class,
];
+275
View File
@@ -0,0 +1,275 @@
<?php
/**
+-----------------------------------------------------------------------------------------------
* GouGuOPEN [ 左手研发,右手开源,未来可期!]
+-----------------------------------------------------------------------------------------------
* @Copyright (c) 2021~2024 http://www.gouguoa.com All rights reserved.
+-----------------------------------------------------------------------------------------------
* @Licensed 勾股OA,开源且可免费使用,但并不是自由软件,未经授权许可不能去除勾股OA的相关版权信息
+-----------------------------------------------------------------------------------------------
* @Author 勾股工作室 <hdm58@qq.com>
+-----------------------------------------------------------------------------------------------
*/
namespace app\project\model;
use think\model;
use think\facade\Db;
use app\api\model\EditLog;
class Project extends Model
{
/**
* 获取分页列表
* @param $where
* @param $param
*/
public function datalist($param=[],$where=[],$whereOr=[])
{
$rows = empty($param['limit']) ? get_config('app.page_size') : $param['limit'];
$order = empty($param['order']) ? 'id desc' : $param['order'];
try {
$list = self::where($where)
->where(function ($query) use ($whereOr) {
if (!empty($whereOr))
$query->whereOr($whereOr);
})
->order($order)
->paginate(['list_rows'=> $rows])
->each(function ($item, $key){
$item['title'] = $item['name'];
$item->director_name = Db::name('Admin')->where(['id' => $item->director_uid])->value('name');
$item->department = Db::name('Department')->where(['id' => $item->did])->value('title');
$item->admin_name = Db::name('Admin')->where(['id' => $item->admin_id])->value('name');
$item->status_name = status_name($item->status);
$item->range_time = date('Y-m-d',$item->start_time). ' 至 ' .date('Y-m-d',$item->end_time);
$item->delay = count_days(date("Y-m-d"),date('Y-m-d', $item->end_time));
$item->cate = Db::name('ProjectCate')->where('id', $item->cate_id)->value('title');
$task_map = [];
$task_map[] = ['project_id', '=', $item->id];
$task_map[] = ['delete_time', '=', 0];
//任务总数
$item->tasks_total = Db::name('ProjectTask')->where($task_map)->count();
//已完成任务
$task_map[] = ['status', '>', 2];
$item->tasks_finish = Db::name('ProjectTask')->where($task_map)->count();
//未完成任务
$item->tasks_unfinish = $item->tasks_total - $item->tasks_finish;
$item->tasks_pensent = "0.00";
if ($item->tasks_total > 0) {
$item->tasks_pensent = round($item->tasks_finish / $item->tasks_total * 100, 2) . "";
}
$step = Db::name('ProjectStep')->where(['project_id' => $item->id,'is_current' => 1,'delete_time'=>0])->find();
if(!empty($step)){
$item->step_director = Db::name('Admin')->where(['id'=>$step['director_uid']])->value('name');
$item->step = $step['title'] . '『' . $item->step_director. '』';
}
else{
$item->step = '-';
}
$item['create_time'] = to_date($item['create_time'],'Y-m-d H:i:s');
});
return $list;
} catch(\Exception $e) {
return ['code' => 1, 'data' => [], 'msg' => $e->getMessage()];
}
}
public function apilist($where, $param)
{
$rows = empty($param['limit']) ? get_config('app.page_size') : $param['limit'];
$order = empty($param['order']) ? 'id desc' : $param['order'];
try {
$list = self::where($where)->field('id,status,name as title')->order($order)->paginate(['list_rows'=> $rows]);
return $list;
} catch(\Exception $e) {
return ['code' => 1, 'data' => [], 'msg' => $e->getMessage()];
}
}
/**
* 添加数据
* @param $param
*/
public function add($param,$step)
{
$insertId = 0;
// 启动事务
Db::startTrans();
try {
$param['create_time'] = time();
$insertId = self::strict(false)->field(true)->insertGetId($param);
$step_users=[];
//项目阶段
foreach ($step as $key => &$value) {
if($key==0){
$value['is_current'] = 1;
}
else{
$value['is_current'] = 0;
}
$value['project_id'] = $insertId;
if(!empty($value['director_uid'])){
$step_users[] = $value['director_uid'];
}
if(!empty($value['uids'])){
$step_users[] = $value['uids'];
}
}
Db::name('ProjectStep')->strict(false)->field(true)->insertAll($step);
$step_users_str = implode(',',$step_users);
//项目成员
$project_users = $param['admin_id'];
if (!empty($param['director_uid'])){
$project_users.=",".$param['director_uid'];
}
if (!empty($param['team_admin_ids'])){
$project_users.=",".$param['team_admin_ids'];
}
if (!empty($step_users_str)){
$project_users.=",".$step_users_str;
}
$project_array = explode(",",(string)$project_users);
$project_array = array_unique($project_array);
$project_user_array=[];
foreach ($project_array as $k => $v) {
if (is_numeric($v)) {
$project_user_array[]=array(
'uid'=>$v,
'admin_id'=>$param['admin_id'],
'project_id'=>$insertId,
'create_time'=>time(),
);
}
}
Db::name('ProjectUser')->strict(false)->field(true)->insertAll($project_user_array);
add_log('add', $insertId, $param);
$log=new EditLog();
$log->add('Project',$insertId);
// 提交事务
Db::commit();
} catch(\Exception $e) {
// 回滚事务
Db::rollback();
return to_assign(1, '操作失败,原因:'.$e->getMessage());
}
return to_assign(0,'操作成功',['return_id'=>$insertId]);
}
/**
* 编辑信息
* @param $param
*/
public function edit($param,$step)
{
// 启动事务
Db::startTrans();
try {
$param['update_time'] = time();
$old = self::find($param['id']);
self::where('id', $param['id'])->strict(false)->field(true)->update($param);
$current_step = Db::name('ProjectStep')->where(['project_id' => $param['id'], 'is_current' => 1,'delete_time'=>0])->value('sort');
//项目阶段
foreach ($step as $key => $value) {
$value['project_id'] = $param['id'];
if($value['id'] == 0){
Db::name('ProjectStep')->strict(false)->field(true)->insert($value);
}
else{
$value['update_time'] = time();
if($value['sort'] == $current_step){
$value['is_current'] = 1;
}
else{
$value['is_current'] = 0;
}
Db::name('ProjectStep')->strict(false)->field(true)->update($value);
}
}
add_log('edit', $param['id'], $param);
$log=new EditLog();
$log->edit('Project',$param['id'],$param,$old);
// 提交事务
Db::commit();
} catch(\Exception $e) {
// 回滚事务
Db::rollback();
return to_assign(1, '操作失败,原因:'.$e->getMessage());
}
return to_assign(0,'操作成功',['return_id'=>$param['id']]);
}
public function apiedit($param)
{
try {
$param['update_time'] = time();
$old = self::find($param['id']);
self::where('id', $param['id'])->strict(false)->field(true)->update($param);
add_log('edit', $param['id'], $param,'项目');
$log=new EditLog();
$log->edit('Project',$param['id'],$param,$old);
} catch(\Exception $e) {
return to_assign(1, '操作失败,原因:'.$e->getMessage());
}
return to_assign(0,'操作成功',['return_id'=>$param['id']]);
}
/**
* 根据id获取信息
* @param $id
*/
public function getById($id)
{
$info = self::find($id);
$info['admin_name'] = Db::name('Admin')->where(['id' => $info['admin_id']])->value('name');
$info['director_name'] = Db::name('Admin')->where(['id' => $info['director_uid']])->value('name');
$info['department'] = Db::name('Department')->where(['id' => $info['did']])->value('title');
$info['contract_name'] = Db::name('Contract')->where(['id' => $info['contract_id']])->value('name');
$info['create_time'] = to_date($info['create_time'],'Y-m-d H:i:s');
$info['update_time'] = to_date($info['update_time'],'Y-m-d H:i:s');
//项目阶段
$step_array = Db::name('ProjectStep')
->field('s.*,a.name as director_name')
->alias('s')
->join('Admin a', 'a.id = s.director_uid', 'LEFT')
->order('s.sort asc')
->where(array('s.project_id' => $id, 's.delete_time' => 0))
->select()->toArray();
foreach ($step_array as $kk => &$vv) {
$unames = Db::name('Admin')->where([['id','in',$vv['uids']]])->column('name');
$vv['unames'] = implode(',',$unames);
}
$info['step_array'] = $step_array;
return $info;
}
/**
* 删除信息
* @param $id
* @param $type
* @return array
*/
public function delById($id,$type=0)
{
if($type==0){
//逻辑删除
try {
$param['delete_time'] = time();
self::where('id', $id)->update(['delete_time'=>time()]);
add_log('delete', $id);
} catch(\Exception $e) {
return to_assign(1, '操作失败,原因:'.$e->getMessage());
}
}
else{
//物理删除
try {
self::destroy($id);
add_log('delete', $id);
} catch(\Exception $e) {
return to_assign(1, '操作失败,原因:'.$e->getMessage());
}
}
return to_assign();
}
}
+108
View File
@@ -0,0 +1,108 @@
<?php
/**
+-----------------------------------------------------------------------------------------------
* GouGuOPEN [ 左手研发,右手开源,未来可期!]
+-----------------------------------------------------------------------------------------------
* @Copyright (c) 2021~2024 http://www.gouguoa.com All rights reserved.
+-----------------------------------------------------------------------------------------------
* @Licensed 勾股OA,开源且可免费使用,但并不是自由软件,未经授权许可不能去除勾股OA的相关版权信息
+-----------------------------------------------------------------------------------------------
* @Author 勾股工作室 <hdm58@qq.com>
+-----------------------------------------------------------------------------------------------
*/
namespace app\project\model;
use think\model;
use think\facade\Db;
class ProjectCate extends Model
{
/**
* 获取分页列表
* @param $where
* @param $param
*/
public function datalist($where, $param)
{
$rows = empty($param['limit']) ? get_config('app.page_size') : $param['limit'];
$order = empty($param['order']) ? 'id desc' : $param['order'];
try {
$list = self::where($where)->order($order)->paginate(['list_rows'=> $rows]);
return $list;
} catch(\Exception $e) {
return ['code' => 1, 'data' => [], 'msg' => $e->getMessage()];
}
}
/**
* 添加数据
* @param $param
*/
public function add($param)
{
$insertId = 0;
try {
$param['create_time'] = time();
$insertId = self::strict(false)->field(true)->insertGetId($param);
add_log('add', $insertId, $param);
} catch(\Exception $e) {
return to_assign(1, '操作失败,原因:'.$e->getMessage());
}
return to_assign(0,'操作成功',['return_id'=>$insertId]);
}
/**
* 编辑信息
* @param $param
*/
public function edit($param)
{
try {
$param['update_time'] = time();
self::where('id', $param['id'])->strict(false)->field(true)->update($param);
add_log('edit', $param['id'], $param);
} catch(\Exception $e) {
return to_assign(1, '操作失败,原因:'.$e->getMessage());
}
return to_assign(0,'操作成功',['return_id'=>$param['id']]);
}
/**
* 根据id获取信息
* @param $id
*/
public function getById($id)
{
$info = self::find($id);
return $info;
}
/**
* 删除信息
* @param $id
* @param $type
* @return array
*/
public function delById($id,$type=0)
{
if($type==0){
//逻辑删除
try {
$param['delete_time'] = time();
self::where('id', $id)->update(['delete_time'=>time()]);
add_log('delete', $id);
} catch(\Exception $e) {
return to_assign(1, '操作失败,原因:'.$e->getMessage());
}
}
else{
//物理删除
try {
self::destroy($id);
add_log('delete', $id);
} catch(\Exception $e) {
return to_assign(1, '操作失败,原因:'.$e->getMessage());
}
}
return to_assign();
}
}
+64
View File
@@ -0,0 +1,64 @@
<?php
/**
+-----------------------------------------------------------------------------------------------
* GouGuOPEN [ 左手研发,右手开源,未来可期!]
+-----------------------------------------------------------------------------------------------
* @Copyright (c) 2021~2024 http://www.gouguoa.com All rights reserved.
+-----------------------------------------------------------------------------------------------
* @Licensed 勾股OA,开源且可免费使用,但并不是自由软件,未经授权许可不能去除勾股OA的相关版权信息
+-----------------------------------------------------------------------------------------------
* @Author 勾股工作室 <hdm58@qq.com>
+-----------------------------------------------------------------------------------------------
*/
declare (strict_types = 1);
namespace app\Project\model;
use think\facade\Db;
use think\Model;
class ProjectDocument extends Model
{
public function datalist($param,$where,$whereOr)
{
$rows = empty($param['limit']) ? get_config('app.page_size') : $param['limit'];
$order = empty($param['order']) ? 'id desc' : $param['order'];
try {
$list = self::where($where)
->where(function ($query) use($whereOr) {
if (!empty($whereOr)){
$query->whereOr($whereOr);
}
})
->order($order)
->paginate(['list_rows'=> $rows])
->each(function ($item, $key){
$item->admin_name = Db::name('Admin')->where(['id' => $item->admin_id])->value('name');
if ($item->project_id > 0) {
$item->project = Db::name('Project')->where(['id' => $item->project_id])->value('name');
}
else{
$item->project = '-';
}
$item['create_time'] = to_date($item['create_time']);
});
return $list;
} catch(\Exception $e) {
return ['code' => 1, 'data' => [], 'msg' => $e->getMessage()];
}
}
//详情
public function detail($id)
{
$detail = Db::name('ProjectDocument')->where(['id' => $id])->find();
if (!empty($detail)) {
$detail['project_name'] = '-';
if ($detail['project_id'] > 0) {
$detail['project_name'] = Db::name('Project')->where(['id' => $detail['project_id']])->value('name');
}
$detail['admin_name'] = Db::name('Admin')->where(['id' => $detail['admin_id']])->value('name');
$detail['times'] = time_trans($detail['create_time']);
$detail['create_time'] = to_date($detail['create_time']);
}
return $detail;
}
}
+118
View File
@@ -0,0 +1,118 @@
<?php
/**
+-----------------------------------------------------------------------------------------------
* GouGuOPEN [ 左手研发,右手开源,未来可期!]
+-----------------------------------------------------------------------------------------------
* @Copyright (c) 2021~2024 http://www.gouguoa.com All rights reserved.
+-----------------------------------------------------------------------------------------------
* @Licensed 勾股OA,开源且可免费使用,但并不是自由软件,未经授权许可不能去除勾股OA的相关版权信息
+-----------------------------------------------------------------------------------------------
* @Author 勾股工作室 <hdm58@qq.com>
+-----------------------------------------------------------------------------------------------
*/
namespace app\project\model;
use think\facade\Db;
use think\Model;
class ProjectTask extends Model
{
//列表
function datalist($param=[],$where=[],$whereOr=[]) {
$rows = empty($param['limit']) ? get_config('app.page_size') : $param['limit'];
$order = empty($param['order']) ? 'status asc,id desc' : $param['order'];
try {
$list = self::where($where)
->where(function ($query) use ($whereOr) {
if (!empty($whereOr))
$query->whereOr($whereOr);
})
->withoutField('content')
->order($order)
->paginate(['list_rows'=> $rows])
->each(function ($item, $key){
$item['director_name'] = '-';
if ($item['director_uid'] > 0) {
$item['director_name'] = Db::name('Admin')->where(['id' => $item['director_uid']])->value('name');
}
$assist_admin_names = Db::name('Admin')->where([['id', 'in', $item['assist_admin_ids']]])->column('name');
if (empty($assist_admin_names)) {
$item['assist_admin_names'] = '-';
} else {
$item['assist_admin_names'] = implode(',', $assist_admin_names);
}
$item['cate_name'] = Db::name('WorkCate')->where(['id' => $item['work_id']])->value('title');
if ($item['project_id'] == 0) {
$item['project_name'] = '-';
} else {
$item['project_name'] = Db::name('Project')->where(['id' => $item['project_id']])->value('name');
}
$item['delay'] = 0;
if ($item['end_time'] > 0) {
$item['end_time'] = date('Y-m-d', $item['end_time']);
if ($item['over_time'] > 0 && $item['status'] < 4) {
$item['delay'] = count_days($item['end_time'], date('Y-m-d', $item['over_time']));
}
if ($item['over_time'] == 0 && $item['status'] < 4) {
$item['delay'] = count_days($item['end_time']);
}
}
else{
$item['delay'] = 9999;
$item['end_time'] = '-';
}
$item['priority_name'] = priority_name($item['priority']);
$item['status_name'] = status_task_name($item['status']);
return $item;
});
return $list;
} catch(\Exception $e) {
return ['code' => 1, 'data' => [], 'msg' => $e->getMessage()];
}
}
//详情
public function detail($id)
{
$detail = Db::name('ProjectTask')->where(['id' => $id])->find();
if (!empty($detail)) {
$detail['project_name'] = '';
if ($detail['project_id'] > 0) {
$detail['project_name'] = Db::name('Project')->where(['id' => $detail['project_id']])->value('name');
}
$detail['admin_name'] = Db::name('Admin')->where(['id' => $detail['admin_id']])->value('name');
$detail['work_hours'] = Db::name('Schedule')->where(['delete_time' => 0, 'tid' => $detail['id']])->sum('labor_time');
$detail['cate_name'] = Db::name('WorkCate')->where(['id' => $detail['work_id']])->value('title');
$detail['director_name']= '';
if($detail['director_uid'] > 0){
$detail['director_name'] = Db::name('Admin')->where(['id' => $detail['director_uid']])->value('name');
}
$detail['schedules'] = Db::name('Schedule')->where(['tid' => $detail['id'], 'delete_time' => 0])->count();
$detail['logs'] = Db::name('EditLog')->where(['name' => 'Task', 'action_id' => $detail['id']])->count();
$detail['comments'] = Db::name('Comment')->where(['module' => 'task', 'delete_time' => 0, 'topic_id' => $detail['id']])->count();
$detail['assist_admin_names'] = '';
if (!empty($detail['assist_admin_ids'])) {
$assist_admin_names = Db::name('Admin')->where('id', 'in', $detail['assist_admin_ids'])->column('name');
$detail['assist_admin_names'] = implode(',', $assist_admin_names);
}
$detail['priority_name'] = priority_name($detail['priority']);
$detail['status_name'] = status_task_name($detail['status']);
$detail['times'] = time_trans($detail['create_time']);
$detail['delay'] = 0;
if($detail['end_time']>0){
$detail['end_time'] = date('Y-m-d', $detail['end_time']);
if ($detail['over_time'] > 0 && $detail['status'] < 4) {
$detail['delay'] = count_days($detail['end_time'], date('Y-m-d', $detail['over_time']));
}
if ($detail['over_time'] == 0 && $detail['status'] < 4) {
$detail['delay'] = count_days($detail['end_time']);
}
}
else{
$detail['end_time'] = '';
}
}
return $detail;
}
}
+108
View File
@@ -0,0 +1,108 @@
<?php
/**
+-----------------------------------------------------------------------------------------------
* GouGuOPEN [ 左手研发,右手开源,未来可期!]
+-----------------------------------------------------------------------------------------------
* @Copyright (c) 2021~2024 http://www.gouguoa.com All rights reserved.
+-----------------------------------------------------------------------------------------------
* @Licensed 勾股OA,开源且可免费使用,但并不是自由软件,未经授权许可不能去除勾股OA的相关版权信息
+-----------------------------------------------------------------------------------------------
* @Author 勾股工作室 <hdm58@qq.com>
+-----------------------------------------------------------------------------------------------
*/
namespace app\project\model;
use think\model;
use think\facade\Db;
class Step extends Model
{
/**
* 获取分页列表
* @param $where
* @param $param
*/
public function datalist($where, $param)
{
$rows = empty($param['limit']) ? get_config('app.page_size') : $param['limit'];
$order = empty($param['order']) ? 'id desc' : $param['order'];
try {
$list = self::where($where)->order($order)->paginate(['list_rows'=> $rows]);
return $list;
} catch(\Exception $e) {
return ['code' => 1, 'data' => [], 'msg' => $e->getMessage()];
}
}
/**
* 添加数据
* @param $param
*/
public function add($param)
{
$insertId = 0;
try {
$param['create_time'] = time();
$insertId = self::strict(false)->field(true)->insertGetId($param);
add_log('add', $insertId, $param);
} catch(\Exception $e) {
return to_assign(1, '操作失败,原因:'.$e->getMessage());
}
return to_assign(0,'操作成功',['return_id'=>$insertId]);
}
/**
* 编辑信息
* @param $param
*/
public function edit($param)
{
try {
$param['update_time'] = time();
self::where('id', $param['id'])->strict(false)->field(true)->update($param);
add_log('edit', $param['id'], $param);
} catch(\Exception $e) {
return to_assign(1, '操作失败,原因:'.$e->getMessage());
}
return to_assign(0,'操作成功',['return_id'=>$param['id']]);
}
/**
* 根据id获取信息
* @param $id
*/
public function getById($id)
{
$info = self::find($id);
return $info;
}
/**
* 删除信息
* @param $id
* @param $type
* @return array
*/
public function delById($id,$type=0)
{
if($type==0){
//逻辑删除
try {
$param['delete_time'] = time();
self::where('id', $id)->update(['delete_time'=>time()]);
add_log('delete', $id);
} catch(\Exception $e) {
return to_assign(1, '操作失败,原因:'.$e->getMessage());
}
}
else{
//物理删除
try {
self::destroy($id);
add_log('delete', $id);
} catch(\Exception $e) {
return to_assign(1, '操作失败,原因:'.$e->getMessage());
}
}
return to_assign();
}
}
+109
View File
@@ -0,0 +1,109 @@
<?php
/**
+-----------------------------------------------------------------------------------------------
* GouGuOPEN [ 左手研发,右手开源,未来可期!]
+-----------------------------------------------------------------------------------------------
* @Copyright (c) 2021~2024 http://www.gouguoa.com All rights reserved.
+-----------------------------------------------------------------------------------------------
* @Licensed 勾股OA,开源且可免费使用,但并不是自由软件,未经授权许可不能去除勾股OA的相关版权信息
+-----------------------------------------------------------------------------------------------
* @Author 勾股工作室 <hdm58@qq.com>
+-----------------------------------------------------------------------------------------------
*/
namespace app\project\model;
use think\model;
use think\facade\Db;
class WorkCate extends Model
{
/**
* 获取分页列表
* @param $where
* @param $param
*/
public function datalist($where, $param)
{
$rows = empty($param['limit']) ? get_config('app.page_size') : $param['limit'];
$order = empty($param['order']) ? 'id desc' : $param['order'];
try {
$list = self::where($where)->order($order)->paginate(['list_rows'=> $rows]);
return $list;
} catch(\Exception $e) {
return ['code' => 1, 'data' => [], 'msg' => $e->getMessage()];
}
}
/**
* 添加数据
* @param $param
*/
public function add($param)
{
$insertId = 0;
try {
$param['create_time'] = time();
$insertId = self::strict(false)->field(true)->insertGetId($param);
add_log('add', $insertId, $param);
} catch(\Exception $e) {
return to_assign(1, '操作失败,原因:'.$e->getMessage());
}
return to_assign(0,'操作成功',['return_id'=>$insertId]);
}
/**
* 编辑信息
* @param $param
*/
public function edit($param)
{
try {
$param['update_time'] = time();
self::where('id', $param['id'])->strict(false)->field(true)->update($param);
add_log('edit', $param['id'], $param);
} catch(\Exception $e) {
return to_assign(1, '操作失败,原因:'.$e->getMessage());
}
return to_assign(0,'操作成功',['return_id'=>$param['id']]);
}
/**
* 根据id获取信息
* @param $id
*/
public function getById($id)
{
$info = self::find($id);
return $info;
}
/**
* 删除信息
* @param $id
* @param $type
* @return array
*/
public function delById($id,$type=0)
{
if($type==0){
//逻辑删除
try {
$param['delete_time'] = time();
self::where('id', $id)->update(['delete_time'=>time()]);
add_log('delete', $id);
} catch(\Exception $e) {
return to_assign(1, '操作失败,原因:'.$e->getMessage());
}
}
else{
//物理删除
try {
self::destroy($id);
add_log('delete', $id);
} catch(\Exception $e) {
return to_assign(1, '操作失败,原因:'.$e->getMessage());
}
}
return to_assign();
}
}
+43
View File
@@ -0,0 +1,43 @@
<?php
/**
+-----------------------------------------------------------------------------------------------
* GouGuOPEN [ 左手研发,右手开源,未来可期!]
+-----------------------------------------------------------------------------------------------
* @Copyright (c) 2021~2024 http://www.gouguoa.com All rights reserved.
+-----------------------------------------------------------------------------------------------
* @Licensed 勾股OA,开源且可免费使用,但并不是自由软件,未经授权许可不能去除勾股OA的相关版权信息
+-----------------------------------------------------------------------------------------------
* @Author 勾股工作室 <hdm58@qq.com>
+-----------------------------------------------------------------------------------------------
*/
namespace app\project\validate;
use think\Validate;
use think\facade\Db;
class CateValidate extends Validate
{
// 自定义验证规则
protected function checkOne($value,$rule,$data=[])
{
$id = isset($data['id'])?$data['id']:0;
$count = Db::name('ProjectCate')->where([['title','=',$data['title']],['id','<>',$id],['delete_time','=',0]])->count();
return $count == 0 ? true : false;
}
protected $rule = [
'title' => 'require|checkOne',
'id' => 'require',
];
protected $message = [
'title.require' => '名称不能为空',
'title.checkOne' => '同样的名称已经存在',
'id.require' => '缺少更新条件',
];
protected $scene = [
'add' => ['title'],
'edit' => ['title', 'id'],
];
}
+34
View File
@@ -0,0 +1,34 @@
<?php
/**
+-----------------------------------------------------------------------------------------------
* GouGuOPEN [ 左手研发,右手开源,未来可期!]
+-----------------------------------------------------------------------------------------------
* @Copyright (c) 2021~2024 http://www.gouguoa.com All rights reserved.
+-----------------------------------------------------------------------------------------------
* @Licensed 勾股OA,开源且可免费使用,但并不是自由软件,未经授权许可不能去除勾股OA的相关版权信息
+-----------------------------------------------------------------------------------------------
* @Author 勾股工作室 <hdm58@qq.com>
+-----------------------------------------------------------------------------------------------
*/
namespace app\project\validate;
use think\Validate;
class DocumentCheck extends Validate
{
protected $rule = [
'title' => 'require',
'id' => 'require',
];
protected $message = [
'title.require' => '文档标题不能为空',
'id.require' => '缺少更新条件',
];
protected $scene = [
'add' => ['title'],
'edit' => ['id'],
];
}
+43
View File
@@ -0,0 +1,43 @@
<?php
/**
+-----------------------------------------------------------------------------------------------
* GouGuOPEN [ 左手研发,右手开源,未来可期!]
+-----------------------------------------------------------------------------------------------
* @Copyright (c) 2021~2024 http://www.gouguoa.com All rights reserved.
+-----------------------------------------------------------------------------------------------
* @Licensed 勾股OA,开源且可免费使用,但并不是自由软件,未经授权许可不能去除勾股OA的相关版权信息
+-----------------------------------------------------------------------------------------------
* @Author 勾股工作室 <hdm58@qq.com>
+-----------------------------------------------------------------------------------------------
*/
namespace app\project\validate;
use think\Validate;
use think\facade\Db;
class IndexValidate extends Validate
{
// 自定义验证规则
protected function checkOne($value,$rule,$data=[])
{
$id = isset($data['id'])?$data['id']:0;
$count = Db::name('Project')->where([['name','=',$data['name']],['id','<>',$id],['delete_time','=',0]])->count();
return $count == 0 ? true : false;
}
protected $rule = [
'name' => 'require|checkOne',
'id' => 'require',
];
protected $message = [
'name.require' => '项目名称不能为空',
'name.checkOne' => '同样的项目名称已经存在',
'id.require' => '缺少更新条件',
];
protected $scene = [
'add' => ['title'],
'edit' => ['title', 'id'],
];
}
+43
View File
@@ -0,0 +1,43 @@
<?php
/**
+-----------------------------------------------------------------------------------------------
* GouGuOPEN [ 左手研发,右手开源,未来可期!]
+-----------------------------------------------------------------------------------------------
* @Copyright (c) 2021~2024 http://www.gouguoa.com All rights reserved.
+-----------------------------------------------------------------------------------------------
* @Licensed 勾股OA,开源且可免费使用,但并不是自由软件,未经授权许可不能去除勾股OA的相关版权信息
+-----------------------------------------------------------------------------------------------
* @Author 勾股工作室 <hdm58@qq.com>
+-----------------------------------------------------------------------------------------------
*/
namespace app\project\validate;
use think\Validate;
use think\facade\Db;
class StepValidate extends Validate
{
// 自定义验证规则
protected function checkOne($value,$rule,$data=[])
{
$id = isset($data['id'])?$data['id']:0;
$count = Db::name('Step')->where([['title','=',$data['title']],['id','<>',$id],['delete_time','=',0]])->count();
return $count == 0 ? true : false;
}
protected $rule = [
'title' => 'require|checkOne',
'id' => 'require',
];
protected $message = [
'title.require' => '名称不能为空',
'title.checkOne' => '同样的名称已经存在',
'id.require' => '缺少更新条件',
];
protected $scene = [
'add' => ['title'],
'edit' => ['title', 'id'],
];
}
+38
View File
@@ -0,0 +1,38 @@
<?php
/**
+-----------------------------------------------------------------------------------------------
* GouGuOPEN [ 左手研发,右手开源,未来可期!]
+-----------------------------------------------------------------------------------------------
* @Copyright (c) 2021~2024 http://www.gouguoa.com All rights reserved.
+-----------------------------------------------------------------------------------------------
* @Licensed 勾股OA,开源且可免费使用,但并不是自由软件,未经授权许可不能去除勾股OA的相关版权信息
+-----------------------------------------------------------------------------------------------
* @Author 勾股工作室 <hdm58@qq.com>
+-----------------------------------------------------------------------------------------------
*/
namespace app\project\validate;
use think\Validate;
class TaskCheck extends Validate
{
protected $rule = [
'title' => 'require',
'plan_hours' => 'require|regex:/^[0-9]+(.[0-9]{1,1})?$/',
'id' => 'require',
];
protected $message = [
'title.require' => '任务主题不能为空',
'plan_hours.require' => '预估工时不能为空',
'plan_hours.regex' => '预估工时只能是整数或者1位小数的数字',
'id.require' => '缺少更新条件',
];
protected $scene = [
'add' => ['title'],
'hours' => ['plan_hours'],
'edit' => ['id'],
];
}
+43
View File
@@ -0,0 +1,43 @@
<?php
/**
+-----------------------------------------------------------------------------------------------
* GouGuOPEN [ 左手研发,右手开源,未来可期!]
+-----------------------------------------------------------------------------------------------
* @Copyright (c) 2021~2024 http://www.gouguoa.com All rights reserved.
+-----------------------------------------------------------------------------------------------
* @Licensed 勾股OA,开源且可免费使用,但并不是自由软件,未经授权许可不能去除勾股OA的相关版权信息
+-----------------------------------------------------------------------------------------------
* @Author 勾股工作室 <hdm58@qq.com>
+-----------------------------------------------------------------------------------------------
*/
namespace app\project\validate;
use think\Validate;
use think\facade\Db;
class WorkValidate extends Validate
{
// 自定义验证规则
protected function checkOne($value,$rule,$data=[])
{
$id = isset($data['id'])?$data['id']:0;
$count = Db::name('WorkCate')->where([['title','=',$data['title']],['id','<>',$id]])->count();
return $count == 0 ? true : false;
}
protected $rule = [
'title' => 'require|checkOne',
'id' => 'require',
];
protected $message = [
'title.require' => '名称不能为空',
'title.checkOne' => '同样的名称已经存在',
'id.require' => '缺少更新条件',
];
protected $scene = [
'add' => ['title'],
'edit' => ['title', 'id'],
];
}
+45
View File
@@ -0,0 +1,45 @@
{extend name="../../base/view/common/base" /}
<!-- 主体 -->
{block name="body"}
<form class="layui-form p-page">
<h3 class="pb-3">项目分类</h3>
<table class="layui-table layui-table-form">
<tr>
<td class="layui-td-gray">项目分类名称<font>*</font></td>
<td><input type="text" name="title" lay-verify="required" autocomplete="off" placeholder="请输入项目分类名称" lay-reqText="请输入项目分类名称"class="layui-input" value="{$detail.title|default=''}"></td>
<td class="layui-td-gray">排序</td>
<td><input type="text" name="sort" placeholder="请输入排序,数字" autocomplete="off" class="layui-input" value="{$detail.sort|default=''}"></td>
</tr>
</table>
<div class="pt-4">
<input type="hidden" name="id" value="{$detail.id|default=0}"/>
<button class="layui-btn layui-btn-normal" lay-submit="" lay-filter="webform">立即提交</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</form>
{/block}
<!-- /主体 -->
<!-- 脚本 -->
{block name="script"}
<script>
var moduleInit = ['tool'];
function gouguInit() {
var form = layui.form, tool = layui.tool;
//监听提交
form.on('submit(webform)', function (data) {
let callback = function (e) {
layer.msg(e.msg);
if (e.code == 0) {
tool.sideClose(1000);
}
}
let clickbtn = $(this);
tool.post("/project/cate/add", data.field, callback,clickbtn);
return false;
});
}
</script>
{/block}
<!-- /脚本 -->
+124
View File
@@ -0,0 +1,124 @@
{extend name="../../base/view/common/base" /}
<!-- 主体 -->
{block name="body"}
<div class="p-page">
<table class="layui-hide" id="table_project_cate" lay-filter="table_project_cate"></table>
</div>
<script type="text/html" id="toolbarDemo">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-sm add-new" type="button">+ 添加项目分类</button>
</div>
</script>
{/block}
<!-- /主体 -->
<!-- 脚本 -->
{block name="script"}
<script>
const moduleInit = ['tool'];
function gouguInit() {
var table = layui.table, tool = layui.tool;
layui.pageTable = table.render({
elem: "#table_project_cate"
,toolbar: '#toolbarDemo'
,url: "/project/cate/datalist"
,page: false
,limit: 999
,cellMinWidth: 80
,cols: [[
{field:'id',width:80, title: 'ID号', align:'center'}
,{field:'title',title: '类型名称'}
,{field:'status', title: '状态',width:80,align:'center',templet: function(d){
var html1='<span class="green">正常</span>';
var html2='<span class="yellow">禁用</span>';
if(d.status==1){
return html1;
}
else{
return html2;
}
}}
,{width:100,title: '操作', align:'center',ignoreExport:true,templet: function(d){
var html='';
var btn='<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="edit">编辑</a>';
var btn1='<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="disable">禁用</a>';
var btn2='<a class="layui-btn layui-btn-xs" lay-event="open">启用</a>';
if(d.status==1){
html = '<div class="layui-btn-group">'+btn+btn1+'</div>';
}
else{
html = '<div class="layui-btn-group">'+btn+btn2+'</div>';
}
return html;
}}
]]
});
table.on('tool(table_project_cate)',function (obj) {
if(obj.event === 'edit'){
add_cate(obj.data.id,obj.data.title);
}
if(obj.event === 'disable'){
layer.confirm('确定要禁用该记录吗?', {icon: 3, title:'提示'}, function(index){
let callback = function (e) {
layer.msg(e.msg);
if (e.code == 0) {
layui.pageTable.reload();
}
}
tool.post("/project/cate/set", {id:obj.data.id,status: 0}, callback);
layer.close(index);
});
}
if(obj.event === 'open'){
layer.confirm('确定要启用该记录吗?', {icon: 3, title:'提示'}, function(index){
let callback = function (e) {
layer.msg(e.msg);
if (e.code == 0) {
layui.pageTable.reload();
}
}
tool.post("/project/cate/set", {id:obj.data.id,status:1}, callback);
layer.close(index);
});
}
});
$('body').on('click','.add-new',function(){
add_cate(0,'');
});
function add_cate(id,val){
var title = '新增类型';
if(id>0){
title = '编辑类型';
}
layer.prompt({
title: title,
value: val,
yes: function(index, layero) {
// 获取文本框输入的值
var value = layero.find(".layui-layer-input").val();
if (value) {
let callback = function (e) {
layer.msg(e.msg);
if (e.code == 0) {
layui.pageTable.reload();
}
}
tool.post("/project/cate/add", {id: id,title: value}, callback);
layer.close(index);
} else {
layer.msg('请填写类型名称');
}
}
})
}
}
</script>
{/block}
<!-- /脚本 -->
+16
View File
@@ -0,0 +1,16 @@
{extend name="../../base/view/common/base" /}
<!-- 主体 -->
{block name="body"}
<div class="p-page">
<h3 class="pb-3">项目分类详情</h3>
<table class="layui-table layui-table-form">
<tr>
<td class="layui-td-gray">项目分类名称</td>
<td>{$detail.title|default=''}"></td>
<td class="layui-td-gray">排序</td>
<td>{$detail.sort|default=0}</td>
</tr>
</table>
</div>
{/block}
<!-- /主体 -->
+136
View File
@@ -0,0 +1,136 @@
{extend name="../../base/view/common/base" /}
{block name="body"}
<form class="layui-form p-4">
{eq name="id" value ="0"}
<h3 class="pb-3">新建文档</h3>
<table class="layui-table layui-table-form">
<tr>
<td class="layui-td-gray">文档名称<font>*</font></td>
<td>
<input type="text" name="title" lay-verify="required" lay-reqText="请输入文档名称" placeholder="请输入文档名称" class="layui-input" value="">
</td>
<td class="layui-td-gray">关联项目<font>*</font></td>
<td>
<input type="text" name="project_name" placeholder="所属项目" value="{$project_name|default=''}" readonly class="layui-input picker-oa" data-types="project" autocomplete="off" />
<input type="hidden" name="project_id" value="{$project_id}" />
</td>
</tr>
<tr>
<td class="layui-td-gray-2">
<div class="layui-input-inline">相关附件</div>
<div class="layui-input-inline">
<button type="button" class="layui-btn layui-btn-xs" id="uploadBtn"><i class="layui-icon"></i></button>
</div>
</td>
<td colspan="5">
<div class="layui-row" id="uploadBox">
<input type="hidden" data-type="file" name="file_ids" value="{$detail.file_ids|default=''}">
{notempty name="$detail.file_ids"}
{volist name="$detail.file_array" id="vo"}
<div class="layui-col-md4" id="uploadFile{$vo.id}">{:file_card($vo)}</div>
{/volist}
{/notempty}
</div>
</td>
</tr>
<tr>
<td class="layui-td-gray" style="vertical-align:top;">文档内容<font>*</font></td>
<td colspan="5">
<textarea name="content" placeholder="请输入内容" class="layui-textarea" id="container" style="border:0;padding:0"></textarea>
</td>
</tr>
</table>
</div>
{else/}
<h3 class="pb-3">编辑文档</h3>
<table class="layui-table layui-table-form">
<tr>
<td class="layui-td-gray">文档名称<font>*</font>
</td>
<td>
<input type="text" name="title" lay-verify="required" lay-reqText="请输入文档名称" placeholder="请输入文档名称"
class="layui-input" value="{$detail.title}">
</td>
<td class="layui-td-gray">关联项目<font>*</font></td>
<td>
<select id="projectId" name="project_id" lay-filter="project_id">
<option value="">请选择关联项目</option>
{volist name=":get_project()" id="v"}
<option value="{$v.id}" {eq name = "$v.id" value="$detail.project_id"} selected{/eq}>{$v.name}</option>
{/volist}
</select>
</td>
</tr>
<tr>
<td class="layui-td-gray-2">
<div class="layui-input-inline">相关附件</div>
<div class="layui-input-inline">
<button type="button" class="layui-btn layui-btn-xs" id="uploadBtn"><i class="layui-icon"></i></button>
</div>
</td>
<td colspan="5">
<div class="layui-row" id="uploadBox">
<input type="hidden" data-type="file" name="file_ids" value="{$detail.file_ids|default=''}">
{notempty name="$detail.file_ids"}
{volist name="$detail.file_array" id="vo"}
<div class="layui-col-md4" id="uploadFile{$vo.id}">{:file_card($vo)}</div>
{/volist}
{/notempty}
</div>
</td>
</tr>
<tr>
<td class="layui-td-gray" style="vertical-align:top;">文档内容<font>*</font></td>
<td colspan="5">
<textarea name="content" placeholder="请输入内容" class="layui-textarea" id="container" style="border:0;padding:0">{$detail.content}</textarea>
</td>
</tr>
</table>
{/eq}
<div class="pt-4">
<input type="hidden" name="id" value="{$id}" />
<button class="layui-btn layui-btn-normal" lay-submit="" lay-filter="webform">立即提交</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</form>
{/block}
<!-- /主体 -->
<!-- 脚本 -->
{block name="script"}
<script>
const moduleInit = ['tool','uploadPlus','tinymce','oaPicker'];
function gouguInit() {
var form = layui.form,tool=layui.tool,uploadPlus = layui.uploadPlus;
//相关附件上传
var attachment = new uploadPlus();
//编辑器初始化
var editor = layui.tinymce;
var edit = editor.render({
selector: "#container",
images_upload_url: '/api/index/upload/sourse/tinymce',//图片上传接口
height:600
});
//监听提交
form.on('submit(webform)', function (data) {
data.field.content = tinyMCE.editors['container'].getContent();
if (data.field.content == '') {
layer.msg('请先完善文档内容');
return false;
}
let callback = function (e) {
layer.msg(e.msg);
if (e.code == 0) {
tool.sideClose(1000,'documentTable');
}
}
let clickbtn = $(this);
tool.post("/project/document/add", data.field, callback,clickbtn);
return false;
});
}
</script>
{/block}
+116
View File
@@ -0,0 +1,116 @@
{extend name="../../base/view/common/base" /}
<!-- 主体 -->
{block name="body"}
<div class="p-page">
<div class="gg-form-bar border-t border-x">
<form class="layui-form" lay-filter="barsearchform">
<div class="layui-input-inline" style="width:240px;">
<input type="text" name="project_name" placeholder="所属项目" readonly class="layui-input picker-oa" data-types="project" autocomplete="off" />
<input name="project_id" style="display: none;" value="" />
</div>
<div class="layui-input-inline" style="width:300px;">
<input type="text" name="keywords" placeholder="关键字" class="layui-input" autocomplete="off" />
</div>
<div class="layui-input-inline" style="width:150px;">
<button class="layui-btn layui-btn-normal" lay-submit="" lay-filter="table-search"><i class="layui-icon layui-icon-search mr-1"></i>搜索</button>
<button type="reset" class="layui-btn layui-btn-reset" lay-filter="table-reset">清空</button>
</div>
</form>
</div>
<table class="layui-hide" id="document" lay-filter="document"></table>
</div>
<script type="text/html" id="toolbarDemo">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-sm" lay-event="add">+ 新建文档</button>
</div>
</script>
{/block}
<!-- /主体 -->
<!-- 脚本 -->
{block name="script"}
<script>
const moduleInit = ['tool','tablePlus','oaPicker'];
function gouguInit() {
var table = layui.tablePlus, form = layui.form, tool = layui.tool;
layui.documentTable = table.render({
elem: '#document',
title: '文档列表',
toolbar: '#toolbarDemo',
is_excel: true,
cellMinWidth: 80,
height: 'full-114',
url: "/project/document/datalist",
cols: [[
{
field: 'id', title: '文档编号', width: 90, align: 'center', templet: function (d) {
return 'D' + d.id;
}
}
, {
field: 'title', title: '文档主题',minWidth:240, templet: function (d) {
var html = '<a class="side-a" data-href="/project/document/view/id/' + d.id + '">' + d.title + '</a>';
return html;
}
}
, { field: 'project', title: '关联项目', width: 240,}
, { field: 'admin_name', title: '创建人', align: 'center', width: 80 }
, { field: 'create_time', title: '创建时间', align: 'center', width: 150 }
,{
field: 'right',
fixed:'right',
title: '操作',
width: 120,
align: 'center',
templet: function (d) {
var html = '<div class="layui-btn-group">';
var btn1='<span class="layui-btn layui-btn-normal layui-btn-xs" lay-event="view">详细</span>';
var btn2='<span class="layui-btn layui-btn-xs" lay-event="edit">编辑</span>';
var btn3='<span class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</span>';
return html+btn1+btn2+btn3+'</div>';
}
}
]]
});
//触发事件
table.on('toolbar(document)', function(obj){
if (obj.event === 'add') {
tool.side("/project/document/add");
return;
}
});
//监听行工具事件
table.on('tool(document)', function(obj) {
var data = obj.data;
if (obj.event === 'view') {
tool.side("/project/document/view?id="+data.id);
return;
}
if(obj.event === 'edit'){
tool.side('/project/document/add?id='+data.id);
return;
}
if (obj.event === 'del') {
layer.confirm('确定要删除吗?', {
icon: 3,
title: '提示'
}, function(index) {
let callback = function (e) {
layer.msg(e.msg);
if (e.code == 0) {
obj.del();
}
}
tool.delete("/project/document/delete", {id: data.id}, callback);
layer.close(index);
});
}
});
}
</script>
{/block}
<!-- /脚本 -->
+58
View File
@@ -0,0 +1,58 @@
{extend name="../../base/view/common/base" /}
{block name="style"}
<link rel="stylesheet" href="{__GOUGU__}/third_party/prism/prism.css"/>
<style>
.text-detial-ops{line-height: 30px; color:#999; font-size: 12px; padding: 12px 0;}
.text-detial-ops span{margin-right: 20px;}
.text-detial-ops a{margin-right:10px;}
.text-detial-content{padding: 8px 0; color:#333; word-break: break-all; border-top:1px solid #e8e8e8;font-size: 16px!important; line-height: 1.72!important;}
.text-detial-content p{padding: 8px 0;}
.text-detial-content img{max-width:98%!important; margin:0 auto; display:block; border: 1px solid #e6e6e6; -webkit-box-shadow: 0 2px 6px rgba(26,26,26,.08); box-shadow: 0 2px 6px rgba(26,26,26,.08); border-radius: 4px;}
.text-detial-content h1,.text-detial-content h2,.text-detial-content h3,.text-detial-content h4,.text-detial-content h5{margin-top:10px;}
.text-detial-content a{color:#186AF2; font-style:italic;}
.text-detial-content a:hover{text-decoration:underline;}
.text-detial-content p code,.blog-detial-content pre{margin:0 3px;font-size: 14px; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; background: #f6f6f6; padding: 10px; border-radius: 2px;}
.text-detial-content p code{border: 1px solid #eee;padding: 2px 4px;}
.text-detial-content table {border-collapse: collapse; border-spacing: 0; display: block; width: 100%; overflow: auto; word-break: normal;word-break: keep-all; margin-top: 0;margin-bottom: 16px;}
.text-detial-content table tr {background-color: #fff;border-top: 1px solid #ccc;}
.text-detial-content table tr:nth-child(2n) {background-color: #f8f8f8;}
.text-detial-content table td, .blog-detial-content table th { padding: 6px 12px;border: 1px solid #ddd; font-size:14px; }
.text-detial-content table th {font-weight: 800;}
.text-detial-content li {list-style: initial;margin-left: 20px;}
:not(pre)>code[class*=language-], pre[class*=language-]{background:#fff!important;border:1px solid #e8e8e8!important; border-radius:3px;}
.upload-file{padding:12px; background-color:#fff; border:1px solid #eee; margin-bottom:12px;}
.upload-file h3{margin-bottom:12px;}
</style>
{/block}
<!-- 主体 -->
{block name="body"}
<form class="layui-form p-4">
<h1>{$detail.title}</h1>
<div class="text-detial-ops">
<span>{$detail.admin_name}发表于:{$detail.create_time}</span><span>关联项目:{$detail.project_name}</span>
</div>
<div class="text-detial-content">
{$detail.content|raw}
</div>
{notempty name="$detail.file_ids"}
<div class="upload-file layui-row">
<h3>相关附件</h3>
{volist name="$detail.file_array" id="vo"}
<div class="layui-col-md4" id="uploadFile{$vo.id}">{:file_card($vo,'view')}</div>
{/volist}
</div>
{/notempty}
</div>
{/block}
<!-- 脚本 -->
{block name="script"}
<script src="{__GOUGU__}/third_party/prism/prism.js"></script>
<script>
const moduleInit = ['tool'];
function gouguInit() {
var form = layui.form,tool=layui.tool;
}
</script>
{/block}
<!-- /脚本 -->
+266
View File
@@ -0,0 +1,266 @@
{extend name="../../base/view/common/base" /}
<!-- 主体 -->
{block name="body"}
<form class="layui-form p-page">
<h3 class="pb-3">新建项目</h3>
<table class="layui-table layui-table-form">
<tr>
<td class="layui-td-gray">项目名称<font>*</font></td>
<td colspan="3"><input type="text" name="name" lay-verify="required" lay-reqText="请输入项目名称" placeholder="请输入项目名称" class="layui-input" value=""></td>
<td class="layui-td-gray">项目类别<font>*</font></td>
<td>
<select name="cate_id" lay-filter="cate" lay-verify="required" lay-reqText="请选择项目类别">
<option value="">请选择</option>
{volist name = ":get_base_data('ProjectCate')" id="vo"}
<option value="{$vo.id}">{$vo.title}</option>
{/volist}
</select>
</td>
</tr>
<tr>
<td class="layui-td-gray">项目经理<font>*</font></td>
<td>
<input type="text" name="director_name" placeholder="请选择项目经理(负责人)" readonly class="layui-input" value="" lay-verify="required" readonly lay-reqText="请选择项目经理(负责人)">
<input type="hidden" name="director_uid" value="0">
<input type="hidden" name="did" value="0">
</td>
<td class="layui-td-gray">归属部门<font>*</font></td>
<td><input type="text" name="department" value="" autocomplete="off" readonly class="layui-input"></td>
<td class="layui-td-gray">始止日期<span style="color: red">*</span></td>
<td><input type="text" name="range_time" class="layui-input tool-time" data-range="到" readonly placeholder="开始 到 结束" lay-verify="required" lay-reqText="请选择计划完成周期"></td>
</tr>
<tr>
<td class="layui-td-gray">项目成员<span style="color: red">*</span></td>
<td colspan="5">
<input type="text" name="team_admin_names" placeholder="请选择项目成员" readonly class="layui-input picker-admin" data-type="2" value="" lay-verify="required" lay-reqText="请完善项目成员">
<input type="hidden" name="team_admin_ids" class="layui-input" value="">
</td>
</tr>
<tr>
<td class="layui-td-gray">关联合同</td>
<td colspan="5">
<input type="text" class="layui-input picker-oa" data-types="contract" name="contract_name" placeholder="请选择需要关联的合同" readonly value="">
<input type="hidden" class="layui-input" name="contract_id" value="0">
</td>
</tr>
<tr>
<td class="layui-td-gray">项目简介</td>
<td colspan="5">
<textarea name="content" class="layui-textarea"></textarea>
</td>
</tr>
<tr>
<td colspan="6" style="background-color:#FAFAFA;">
<strong>项目阶段</strong><span style="color: red">*</span>
<span class="layui-btn layui-btn-sm" id="stepAdd">+ 添加阶段</span>
</td>
</tr>
<tr>
<td colspan="6">
<table id="stepTable" class="layui-table layui-table-min" style="margin:0">
<tr>
<th width="60">序号</th>
<th width="200">阶段名称</th>
<th width="80">阶段负责人</th>
<th width="180">阶段成员</th>
<th width="162">阶段周期</th>
<th>阶段说明</th>
<th width="120">操作</th>
</tr>
{volist name = ":get_base_data('step')" id="vo" key="k"}
<tr class="step-tr">
<td class="step-key"><strong>{$k}</strong> 阶段</td>
<td>
<div class="layui-input-group">
<input type="text" name="step_title[]" value="{$vo.title}" class="layui-input" lay-verify="required" lay-reqText="请输入阶段名称">
<div class="layui-input-split layui-input-suffix picker-diy" style="cursor: pointer;"><i class="layui-icon layui-icon-search"></i></div>
</div>
</td>
<td>
<input type="text" value="" class="layui-input layui-input-readonly picker-admin" lay-verify="required" lay-reqText="请选择阶段负责人" readonly>
<input type="hidden" name="step_director_uid[]" value="0" readonly>
</td>
<td>
<input type="text" value="" class="layui-input layui-input-readonly picker-admin" data-type="2">
<input type="hidden" name="step_uids[]" value="">
</td>
<td><input type="text" name="step_cycle_time[]" value="" class="layui-input layui-input-readonly tool-time" data-range="到" lay-verify="required" lay-reqText="请选择阶段周期" readonly></td>
<td><input type="text" name="step_remark[]" value="" class="layui-input"></td>
<td>
<input type="hidden" name="step_id[]" value="0" class="layui-input">
<div class="layui-btn-group">
<span class="layui-btn layui-btn-xs step-up">上移</span>
<span class="layui-btn layui-btn-normal layui-btn-xs step-down">下移</span>
<span class="layui-btn layui-btn-danger layui-btn-xs step-del" data-id="0">删除</span>
</div>
</td>
</tr>
{/volist}
</table>
</td>
</tr>
</table>
<div class="pt-3">
<button class="layui-btn layui-btn-normal" lay-submit="" lay-filter="webform">立即提交</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</form>
{/block}
<!-- /主体 -->
<!-- 脚本 -->
{block name="script"}
<script>
var moduleInit = ['tool','oaPicker'];
function gouguInit() {
var form = layui.form, tool = layui.tool,oaPicker = layui.oaPicker;
//选择项目经理
$('body').on('click','[name="director_name"]',function () {
var ids=$('[name="director_uid"]').val(),names=$('[name="director_name"]').val();
oaPicker.employeeInit({
ids:ids,
names:names,
type:1,
callback:function(data){
let select_id=[],select_name=[],select_did=[],select_dname=[];
for(var a=0; a<data.length;a++){
select_id.push(data[a].id);
select_name.push(data[a].name);
select_did.push(data[a].did);
select_dname.push(data[a].department);
}
$('[name="director_uid"]').val(select_id.join(','));
$('[name="director_name"]').val(select_name.join(','));
$('[name="did"]').val(select_did.join(','));
$('[name="department"]').val(select_dname.join(','));
}
});
});
//添加阶段信息表格
$('#stepAdd').on('click',function(){
var html = '';
html += '<tr class="step-tr">\
<td class="step-key">第 <strong>0</strong> 阶段</td>\
<td>\
<div class="layui-input-group">\
<input type="text" name="step_title[]" value="" class="layui-input" lay-verify="required" lay-reqText="请输入阶段名称">\
<div class="layui-input-split layui-input-suffix picker-diy" style="cursor: pointer;"><i class="layui-icon layui-icon-search"></i></div>\
</div>\
</td>\
<td>\
<input type="text" value="" class="layui-input layui-input-readonly picker-admin" lay-verify="required" lay-reqText="请选择阶段负责人" readonly>\
<input type="hidden" name="step_director_uid[]" value="0" readonly>\
</td>\
<td>\
<input type="text" value="" class="layui-input layui-input-readonly picker-admin" data-type="2">\
<input type="hidden" name="step_uids[]" value="">\
</td>\
<td><input type="text" name="step_cycle_time[]" value="" class="layui-input layui-input-readonly tool-time" data-range="到" lay-verify="required" lay-reqText="请选择服务周期" readonly></td>\
<td><input type="text" name="step_remark[]" value="" class="layui-input"></td>\
<td>\
<input type="hidden" name="step_id[]" value="0" class="layui-input">\
<div class="layui-btn-group">\
<span class="layui-btn layui-btn-xs step-up">上移</span>\
<span class="layui-btn layui-btn-normal layui-btn-xs step-down">下移</span>\
<span class="layui-btn layui-btn-danger layui-btn-xs step-del" data-id="0">删除</span>\
</div>\
</td>\
</tr>';
$('#stepTable').append(html).find('.tr-none').remove();
$('#stepTable').find('.step-key').each(function(index,item){
$(this).html('第 <strong>'+(index+1)+'</strong> 阶段');
})
form.render();
});
$('#stepTable').on('click','.step-del',function(){
if($('.step-tr').length<2){
layer.msg('至少保留一个项目阶段');
return false;
}
$(this).parents('.step-tr').remove();
$('#stepTable').find('.step-key').each(function(index,item){
$(this).html('第 <strong>'+(index+1)+'</strong> 阶段');
})
});
$('#stepTable').on('click','.step-up',function(e){
let obj = $(e.target).closest('tr');
tr_pre($(obj[0]));
})
$('#stepTable').on('click','.step-down',function(e){
var obj = $(e.target).closest('tr');
tr_next($(obj[0]));
})
function tr_pre(o) {
var pres = o.prevAll('tr.step-tr');
if (pres.length > 0) {
var tmp = o.clone(true);
var oo = pres[0];
o.remove();
$(oo).before(tmp);
$('#stepTable').find('.step-key').each(function(index,item){
$(this).html('第 <strong>'+(index+1)+'</strong> 阶段');
})
}
else{
layer.msg('已经第一个了');
}
}
function tr_next(o) {
var nexts = o.nextAll('tr.step-tr');
if (nexts.length > 0) {
var tmp = o.clone(true);
var oo = nexts[0];
o.remove();
$(oo).after(tmp);
$('#stepTable').find('.step-key').each(function(index,item){
$(this).html('第 <strong>'+(index+1)+'</strong> 阶段');
})
}else{
layer.msg('已经最后一个了');
}
}
$('body').on('click','.picker-diy',function () {
let that = $(this),ids = [],titles=[],map = {};
let callback = function(data){
for ( var i = 0; i <data.length; i++){
ids.push(data[i].id);
titles.push(data[i].title);
}
that.prev().val(titles.join(','));
}
let opts={
"title":"选择项目阶段",
"url": "/project/api/get_project_step",
"ids":"",
"titles":"",
"area": ['600px', '580px'],
"cols":[{field: 'id',width: 80,title:'序号',align:'center'},{field:'title',title:'项目阶段'}]
}
oaPicker.picker(opts,1,callback,map);//单选
});
//监听提交
form.on('submit(webform)', function (data) {
let callback = function (e) {
layer.msg(e.msg);
if (e.code == 0) {
tool.sideClose(1000);
}
}
let clickbtn = $(this);
tool.post("/project/index/add", data.field, callback,clickbtn);
return false;
});
}
</script>
{/block}
<!-- /脚本 -->
+154
View File
@@ -0,0 +1,154 @@
{extend name="../../base/view/common/base" /}
<!-- 主体 -->
{block name="body"}
<div class="p-page">
<form class="layui-form gg-form-bar border-t border-x" lay-filter="barsearchform">
<div class="layui-input-inline" style="width:100px;">
<select name="status">
<option value="">项目状态</option>
<option value="1">未开始</option>
<option value="2">进行中</option>
<option value="3">已完成</option>
<option value="4">已关闭</option>
</select>
</div>
<div class="layui-input-inline" style="width:136px;">
<select name="cate_id">
<option value="">项目类别</option>
{volist name = ":get_base_data('ProjectCate')" id="vo"}
<option value="{$vo.id}">{$vo.title}</option>
{/volist}
</select>
</div>
<div class="layui-input-inline" style="width:160px;">
<input type="text" name="director" placeholder="项目负责人,可多选" readonly class="layui-input picker-admin" data-type="2" autocomplete="off" />
<input name="director_uid" style="display: none;" value="" />
</div>
<div class="layui-input-inline" style="width:300px">
<input type="text" name="keywords" placeholder="关键字,项目名称/项目描述" class="layui-input" autocomplete="off" />
</div>
<div class="layui-input-inline" style="width:150px">
<button class="layui-btn layui-btn-normal" lay-submit="" lay-filter="table-search"><i class="layui-icon layui-icon-search mr-1"></i>搜索</button>
<button type="reset" class="layui-btn layui-btn-reset" lay-filter="table-reset">清空</button>
</div>
</form>
<table class="layui-hide" id="table_project" lay-filter="table_project"></table>
</div>
<script type="text/html" id="toolbarDemo">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-sm tool-add" type="button" data-href="/project/index/add">+ 添加项目</button>
</div>
</script>
{/block}
<!-- /主体 -->
<!-- 脚本 -->
{block name="script"}
<script>
const auth = {$auth};
const moduleInit = ['tool','tablePlus','oaPicker'];
function gouguInit() {
var table = layui.tablePlus, tool = layui.tool;
layui.pageTable = table.render({
elem: "#table_project"
, toolbar: "#toolbarDemo"
,url: "/project/index/datalist"
,page: true
,limit: 20
,cellMinWidth: 80
,height: 'full-114'
,cols: [[
{field:'id',width:80, title: 'ID号', align:'center'}
,{ field: 'status', title: '项目状态', align: 'center', width: 90, templet: function (d) {
var html = '<span class="check-status-color-' + d.status + '">『' + d.status_name + '』</span>';
return html;
}
}
,{field:'cate',title: '项目类别',width:100,align:'center'}
,{field:'name',title: '项目名称',minWidth:240}
,{field:'director_name',title: '负责人',width:80,align:'center'}
,{ field:'range_time', title: '项目计划周期', align: 'center', width: 200}
,{field:'step',title: '当前阶段及负责人',align: 'center',width: 180}
,{field:'tasks_unfinish', align: 'center', style: 'color: #91CC75;', width: 90, 'title': '进行中任务' }
,{field:'tasks_finish', align: 'center', style: 'color: #FAC858;', width: 90, 'title': '已完成任务' }
,{field:'tasks_pensent', align: 'center', style: 'color: #EE6666;', width: 90, 'title': '任务完成率' }
,{field:'admin_name',title: '创建人',width:80,align:'center'}
,{field:'create_time', title: '创建时间',width:150,align:'center'}
,{width:120,fixed:'right',title: '操作', align:'center',ignoreExport:true,templet: function(d){
var html='';
var btn1='<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="view">详细</a>';
var btn2='<a class="layui-btn layui-btn-xs" lay-event="edit">编辑</a>';
var btn3='<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>';
if(d.status==3){
btn2='';
btn3='<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="affirm">反确认完成</a>';
}
if(auth == 1 || d.admin_id == login_admin || d.director_uid == login_admin){
return '<div class="layui-btn-group">'+btn1+btn2+btn3+'</div>';
}
else{
return btn1
}
}}
]]
});
table.on('tool(table_project)',function (obj) {
var checkStatus = table.checkStatus(obj.config.id); //获取选中行状态
var data = obj.data;
if (obj.event === 'view') {
tool.side("/project/index/view?id="+data.id);
return;
}
if (obj.event === 'edit') {
tool.side("/project/index/add?id="+data.id);
return;
}
if (obj.event === 'affirm') {
let callback = function (e) {
layer.msg(e.msg);
if(e.code==0){
layui.pageTable.reload();
}
}
layer.open({
type: 1,
title: '请输入反确认的原因或理由',
area: ['500px', '252px'],
content: '<div style="padding:5px;"><textarea class="layui-textarea" id="remarkTextarea" style="width: 100%; height:132px;"></textarea></div>',
btnAlign: 'c',
btn: ['确认操作'],
yes: function (index) {
let remark = $("#remarkTextarea").val();
if (remark != '') {
tool.post("/project/api/step_check", {id: data.id,check:3,content:remark}, callback);
layer.close(index);
} else {
layer.msg('请输入原因或理由');
}
}
})
return;
}
if (obj.event === 'del') {
layer.confirm('确定要删除该项目吗?', { icon: 3, title: '提示' }, function (index) {
let callback = function (e) {
layer.msg(e.msg);
if (e.code == 0) {
obj.del();
}
}
tool.delete("/project/index/del", { id: data.id }, callback);
layer.close(index);
});
return;
}
});
}
</script>
{/block}
<!-- /脚本 -->
+297
View File
@@ -0,0 +1,297 @@
{extend name="../../base/view/common/base" /}
<!-- 主体 -->
{block name="body"}
<form class="layui-form p-page">
<h3 class="pb-3">编辑项目</h3>
<table class="layui-table layui-table-form">
<tr>
<td class="layui-td-gray">项目名称<font>*</font></td>
<td colspan="3"><input type="text" name="name" lay-verify="required" lay-reqText="请输入项目名称" placeholder="请输入项目名称" class="layui-input" value="{$detail.name}"></td>
<td class="layui-td-gray">项目类别<font>*</font></td>
<td>
<select name="cate_id" lay-verify="required" lay-reqText="请选择项目类别">
<option value="">请选择</option>
{volist name = ":get_base_data('ProjectCate')" id="vo"}
<option value="{$vo.id}" {eq name="$vo.id" value="$detail.cate_id"} selected{/eq}>{$vo.title}</option>
{/volist}
</select>
</td>
</tr>
<tr>
<td class="layui-td-gray">项目经理<font>*</font></td>
<td>
<input type="text" name="director_name" placeholder="请选择项目经理(负责人)" readonly class="layui-input" value="{$detail.director_name}" lay-verify="required" readonly lay-reqText="请选择项目经理(负责人)">
<input type="hidden" name="director_uid" value="{$detail.director_uid}">
<input type="hidden" name="did" value="{$detail.did}">
</td>
<td class="layui-td-gray">归属部门<font>*</font></td>
<td><input type="text" name="department" value="{$detail.department}" autocomplete="off" readonly class="layui-input"></td>
<td class="layui-td-gray">始止日期<span style="color: red">*</span></td>
<td><input type="text" name="range_time" value="{$detail.start_time|date='Y-m-d'} 到 {$detail.end_time|date='Y-m-d'}" class="layui-input tool-time" data-range="到" readonly placeholder="开始 到 结束" lay-verify="required" lay-reqText="请选择计划完成周期"></td>
</tr>
<tr>
<td class="layui-td-gray">关联合同</td>
<td colspan="5">
<input type="text" class="layui-input picker-oa" data-types="contract" name="contract_name" placeholder="请选择需要关联的合同" readonly value="{$detail.contract_name|default=''}">
<input type="hidden" class="layui-input" name="contract_id" value="{$detail.contract_id}">
</td>
</tr>
<tr>
<td class="layui-td-gray">项目简介</td>
<td colspan="5">
<textarea name="content" class="layui-textarea">{$detail.content|default=''}</textarea>
</td>
</tr>
{lt name="$detail.status" value="3"}
<tr>
<td colspan="6" style="background-color:#FAFAFA;"><strong>项目阶段</strong><span style="color: red">*</span> <span class="layui-btn layui-btn-xs" id="stepAdd">+ 添加阶段</span></td>
</tr>
<tr>
<td colspan="6">
<table id="stepTable" class="layui-table layui-table-min" style="margin:0">
<tr>
<th width="60">序号</th>
<th width="180">阶段名称</th>
<th width="80">阶段负责人</th>
<th width="180">阶段成员</th>
<th width="162">阶段周期</th>
<th>阶段说明</th>
<th width="120">操作</th>
</tr>
{volist name="$detail.step_array" id="vo" key="k"}
{egt name="$vo.sort" value="$detail.current_step"}
<tr class="step-tr">
<td class="step-key"><strong>{$k}</strong> 阶段</td>
<td>
<div class="layui-input-group">
<input type="text" name="step_title[]" value="{$vo.title}" class="layui-input" lay-verify="required" lay-reqText="请输入阶段名称">
<div class="layui-input-split layui-input-suffix picker-diy" style="cursor: pointer;"><i class="layui-icon layui-icon-search"></i></div>
</div>
</td>
<td>
<input type="text" value="{$vo.director_name}" class="layui-input layui-input-readonly picker-admin" lay-verify="required" lay-reqText="请选择阶段负责人" readonly>
<input type="hidden" name="step_director_uid[]" value="{$vo.director_uid}" readonly>
</td>
<td>
<input type="text" value="{$vo.unames}" class="layui-input layui-input-readonly picker-admin" data-type="2">
<input type="hidden" name="step_uids[]" value="{$vo.uids}">
</td>
<td><input type="text" name="step_cycle_time[]" value="{$vo.start_time|date='Y-m-d'} 到 {$vo.end_time|date='Y-m-d'}" class="layui-input layui-input-readonly tool-time" data-range="到" lay-verify="required" lay-reqText="请选择阶段周期" readonly></td>
<td><input type="text" name="step_remark[]" value="{$vo.remark|default=''}" class="layui-input"></td>
<td>
<input type="hidden" name="step_id[]" value="{$vo.id}" class="layui-input">
<div class="layui-btn-group">
<span class="layui-btn layui-btn-xs step-up">上移</span>
<span class="layui-btn layui-btn-normal layui-btn-xs step-down">下移</span>
<span class="layui-btn layui-btn-danger layui-btn-xs step-del" data-id="{$vo.id}">删除</span>
</div>
</td>
</tr>
{else/}
<tr>
<td class="step-key"><strong>{$k}</strong> 阶段</td>
<td><input type="text" name="step_title[]" value="{$vo.title}" class="layui-input layui-input-readonly" readonly lay-verify="required" lay-reqText="请输入阶段名称"></td>
<td>
<input type="text" value="{$vo.director_name}" class="layui-input layui-input-readonly" lay-verify="required" lay-reqText="请选择阶段负责人" readonly>
<input type="hidden" name="step_director_uid[]" value="{$vo.director_uid}" readonly>
</td>
<td>
<input type="text" value="{$vo.unames}" class="layui-input layui-input-readonly" data-type="2">
<input type="hidden" name="step_uids[]" value="{$vo.uids}">
</td>
<td><input type="text" name="step_cycle_time[]" value="{$vo.start_time|date='Y-m-d'} 到 {$vo.end_time|date='Y-m-d'}" class="layui-input layui-input-readonly" data-range="到" lay-verify="required" lay-reqText="请选择阶段周期" readonly></td>
<td><input type="text" name="step_remark[]" value="{$vo.remark|default=''}" readonly class="layui-input layui-input-readonly"></td>
<td>
<input type="hidden" name="step_id[]" value="{$vo.id}" class="layui-input">
-
</td>
</tr>
{/egt}
{/volist}
</table>
</td>
</tr>
{/lt}
</table>
<div class="pt-3">
<input type="hidden" name="id" value="{$detail.id|default=0}"/>
<button class="layui-btn layui-btn-normal" lay-submit="" lay-filter="webform">立即提交</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</form>
{/block}
<!-- /主体 -->
<!-- 脚本 -->
{block name="script"}
<script>
var moduleInit = ['tool','oaPicker'];
function gouguInit() {
var form = layui.form, tool = layui.tool,oaPicker = layui.oaPicker;
//选择项目经理
$('body').on('click','[name="director_name"]',function () {
var ids=$('[name="director_uid"]').val(),names=$('[name="director_name"]').val();
oaPicker.employeeInit({
ids:ids,
names:names,
type:1,
callback:function(data){
let select_id=[],select_name=[],select_did=[],select_dname=[];
for(var a=0; a<data.length;a++){
select_id.push(data[a].id);
select_name.push(data[a].name);
select_did.push(data[a].did);
select_dname.push(data[a].department);
}
$('[name="director_uid"]').val(select_id.join(','));
$('[name="director_name"]').val(select_name.join(','));
$('[name="did"]').val(select_did.join(','));
$('[name="department"]').val(select_dname.join(','));
}
});
});
//添加阶段信息表格
$('#stepAdd').on('click',function(){
var html = '';
html += '<tr class="step-tr">\
<td class="step-key">第 <strong>0</strong> 阶段</td>\
<td>\
<div class="layui-input-group">\
<input type="text" name="step_title[]" value="" class="layui-input" lay-verify="required" lay-reqText="请输入阶段名称">\
<div class="layui-input-split layui-input-suffix picker-diy" style="cursor: pointer;"><i class="layui-icon layui-icon-search"></i></div>\
</div>\
</td>\
<td>\
<input type="text" value="" class="layui-input layui-input-readonly picker-admin" lay-verify="required" lay-reqText="请选择阶段负责人" readonly>\
<input type="hidden" name="step_director_uid[]" value="0" readonly>\
</td>\
<td>\
<input type="text" value="" class="layui-input layui-input-readonly picker-admin" data-type="2">\
<input type="hidden" name="step_uids[]" value="">\
</td>\
<td><input type="text" name="step_cycle_time[]" value="" class="layui-input layui-input-readonly tool-time" data-range="到" lay-verify="required" lay-reqText="请选择服务周期" readonly></td>\
<td><input type="text" name="step_remark[]" value="" class="layui-input"></td>\
<td>\
<input type="hidden" name="step_id[]" value="0" class="layui-input">\
<div class="layui-btn-group">\
<span class="layui-btn layui-btn-xs step-up">上移</span>\
<span class="layui-btn layui-btn-normal layui-btn-xs step-down">下移</span>\
<span class="layui-btn layui-btn-danger layui-btn-xs step-del" data-id="0">删除</span>\
</div>\
</td>\
</tr>';
$('#stepTable').append(html).find('.tr-none').remove();
$('#stepTable').find('.step-key').each(function(index,item){
$(this).html('第 <strong>'+(index+1)+'</strong> 阶段');
})
form.render();
});
$('#stepTable').on('click','.layui-btn-danger',function(){
if($('.step-tr').length<2){
layer.msg('至少保留一个项目阶段');
return false;
}
let that=$(this);
let id = that.data('id');
if(id==0){
that.parents('.step-tr').remove();
$('#stepTable').find('.step-key').each(function(index,item){
$(this).html('第 <strong>'+(index+1)+'</strong> 阶段');
})
}
else{
layer.confirm('确定要删除该项目阶段吗?', { icon: 3, title: '提示' }, function (index) {
let callback = function (e) {
layer.msg(e.msg);
if (e.code == 0) {
that.parents('.step-tr').remove();
$('#stepTable').find('.step-key').each(function(index,item){
$(this).html('第 <strong>'+(index+1)+'</strong> 阶段');
})
}
}
tool.delete("/project/api/step_del", { id: id }, callback);
layer.close(index);
});
}
});
$('#stepTable').on('click','.step-up',function(e){
let obj = $(e.target).closest('tr');
tr_pre($(obj[0]));
})
$('#stepTable').on('click','.step-down',function(e){
var obj = $(e.target).closest('tr');
tr_next($(obj[0]));
})
function tr_pre(o) {
var pres = o.prevAll('tr.step-tr');
if (pres.length > 0) {
var tmp = o.clone(true);
var oo = pres[0];
o.remove();
$(oo).before(tmp);
$('#stepTable').find('.step-key').each(function(index,item){
$(this).html('第 <strong>'+(index+1)+'</strong> 阶段');
})
}
else{
layer.msg('已经第一个了');
}
}
function tr_next(o) {
var nexts = o.nextAll('tr.step-tr');
if (nexts.length > 0) {
var tmp = o.clone(true);
var oo = nexts[0];
o.remove();
$(oo).after(tmp);
$('#stepTable').find('.step-key').each(function(index,item){
$(this).html('第 <strong>'+(index+1)+'</strong> 阶段');
})
}else{
layer.msg('已经最后一个了');
}
}
$('body').on('click','.picker-diy',function () {
let that = $(this),ids = [],titles=[],map = {};
let callback = function(data){
for ( var i = 0; i <data.length; i++){
ids.push(data[i].id);
titles.push(data[i].title);
}
that.prev().val(titles.join(','));
}
let opts={
"title":"选择项目阶段",
"url": "/project/api/get_project_step",
"ids":"",
"titles":"",
"area": ['600px', '580px'],
"cols":[{field: 'id',width: 80,title:'序号',align:'center'},{field:'title',title:'项目阶段'}]
}
oaPicker.picker(opts,1,callback,map);//单选
});
//监听提交
form.on('submit(webform)', function (data) {
let callback = function (e) {
layer.msg(e.msg);
if (e.code == 0) {
tool.sideClose(1000);
}
}
let clickbtn = $(this);
tool.post("/project/index/add", data.field, callback,clickbtn);
return false;
});
}
</script>
{/block}
<!-- /脚本 -->
+552
View File
@@ -0,0 +1,552 @@
{extend name="../../base/view/common/base" /}
{block name="style"}
<style>
.layui-tab-title .layui-this{background-color:#fff;}
.layui-tab-card,.layui-card{box-shadow:0 0 0 0 rgb(0 0 0 / 10%); border-radius:0;}
.layui-card-tips {color: #969696;}
.layui-card-value {padding: 4px 0 5px;font-size: 18px;color: #1E9FFF;}
.check-items{overflow-x: auto; padding: 2px 0;}
.check-item .check-item-icon strong{margin-right:3px; font-size:36px;}
.check-item strong.blue,.check-item strong.gray{display:none;}
.check-item.blue strong.black,.check-item.gray strong.black{display:none;}
.check-item.blue strong.blue{display:block;}
.check-item.gray strong.gray{display:block;}
.flow-flex-row {box-direction: row;
box-orient: horizontal;
-webkit-box-orient: horizontal;
-ms-flex-direction: row;
flex-direction: row;
}
.flow-flexbox { width: 100%;text-align: left;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
display: -webkit-flex;
box-align: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
flex-wrap: wrap;
}
.check-item{width: auto; -ms-flex-negative: 0; flex-shrink: 0; padding:8px 0;}
.check-item i{font-size:20px; margin-right:3px;}
.layui-icon[data-ok]{color:#34a853}
.layui-icon[data-no]{color:#FF5722;}
.layui-icon[data-on]{color:#4285f4;}
.check-item-time{color:#969696; font-size:12px; margin-left:3px;}
.check-item .layui-icon.layui-icon-right{font-size:20px;}
.check-item:last-child .layui-icon-right{display:none;}
.file-card{line-height:normal;}
</style>
{/block}
<!-- 主体 -->
{block name="body"}
<div class="p-page">
<h2 class="pb-3">
<span class="font20">{$detail.name}</span>
{gt name="$role" value="0"}
{eq name="$detail.status" value="4"}
<span class="layui-btn layui-btn-xs" data-event="open">开启</span>
{else/}
<span class="layui-btn layui-btn-danger layui-btn-xs" data-event="close">关闭</span>
{/eq}
{/gt}
</h2>
<div class="pb-2">
<span class="layui-badge layui-bg-gray">#T{$detail.id}</span>
<span class="mx-2">{$detail.admin_name}</span>
<span class="gray">创建于{$detail.create_time}<span>{gt name="$detail.update_time" value="0"},最近更新于 {$detail.update_time}{/gt}</span></span>
</div>
<div class="layui-tabs layui-tab-brief" id="projectTab">
<ul class="layui-tabs-header border-t border-x bg-white">
<li class="layui-this" data-load="true">项目概览</li>
<li data-load="">项目任务</li>
<li data-load="">工作记录</li>
<li data-load="">项目文档</li>
<li data-load="">项目人员</li>
<li data-load="">项目动态</li>
<li data-load="">项目评论</li>
</ul>
<div class="layui-tabs-body" style="padding:0">
<div class="layui-tabs-item layui-show">
{include file="/index/view_overview" /}
</div>
<div class="layui-tabs-item">
{include file="/index/view_task" /}
</div>
<div class="layui-tabs-item">
{include file="/index/view_schedule" /}
</div>
<div class="layui-tabs-item">
{include file="/index/view_document" /}
</div>
<div class="layui-tabs-item">
{include file="/index/view_user" /}
</div>
<div class="layui-tabs-item">
{include file="/index/view_log" /}
</div>
<div class="layui-tabs-item">
{include file="/index/view_comment" /}
</div>
</div>
</div>
</div>
{/block}
<!-- /主体 -->
<!-- 脚本 -->
{block name="script"}
<script src="{__GOUGU__}/third_party/echart/echarts.min.js"></script>
<script>
const project_id = '{$detail.id}';
const project_start_time = "{$detail.start_time|date='Y-m-d'}";
var chartProgress = document.getElementById('progress');
var progressChart = echarts.init(chartProgress);
var optionA;
optionA = {
backgroundColor: "#ffffff",
title: {
text: '67.45%',//主标题文本
subtext: '任务完成率',//副标题文本
x: 'center',
y: '39%',
textStyle: {
fontWeight: 'normal',
fontSize: 18,
color: '#FF974C',
align: 'center'
},
subtextStyle: {
fontSize: 12,
color: '#6c7a89',
}
},
tooltip: {
trigger: "item",
formatter: '{b}<br/><strong>{c}</strong>',
show: true,
},
series: [
{
type: 'pie',
radius: ['60%', '80%'],
center: ['50%', '50%'],
avoidLabelOverlap: false,
label: {
show: false
},
data: [
{ value: 1048, name: '待处理' },
{ value: 735, name: '已完成' }
]
}
]
};
var chartDelay = document.getElementById('delay');
var delayChart = echarts.init(chartDelay);
var optionB;
optionB = {
backgroundColor: "#ffffff",
title: {
text: '40.25%',//主标题文本
subtext: '任务延迟率',//副标题文本
x: 'center',
y: '39%',
textStyle: {
fontWeight: 'normal',
fontSize: 18,
color: '#FF974C',
align: 'center',
marginLeft: '-10px'
},
subtextStyle: {
fontSize: 12,
color: '#6c7a89',
}
},
tooltip: {
trigger: "item",
formatter: '{b}<br/><strong>{c}</strong>',
show: true,
},
series: [
{
type: 'pie',
radius: ['60%', '80%'],
center: ['50%', '50%'],
avoidLabelOverlap: false,
label: {
show: false
},
data: [{
value: 1048,
name: '延迟',
itemStyle: {
color: "#ED6666",
}
},
{
value: 735,
name: '未延迟',
itemStyle: {
color: "#91CC75",
}
}
]
}
]
};
var chartCross = document.getElementById('cross');
var crossChart = echarts.init(chartCross);
var optionD;
optionD = {
backgroundColor: "#ffffff",
color: ['#8C92A4', '#2C7EF8'],
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
label: {
backgroundColor: '#6a7985'
}
}
},
legend: {
data: ['任务计划剩余', '任务实际剩余']
},
grid: {
top: 36,
left: 8,
right: 36,
bottom: 0,
containLabel: true
},
xAxis: [
{
type: 'category',
boundaryGap: false,
splitLine: {
show: true,
lineStyle: {
type: 'dashed'
}
}
}
],
yAxis: [{
axisLine: {
show: true
},
boundaryGap: false,
splitLine: {
show: true,
lineStyle: {
type: 'dashed'
}
},
type: 'value'
}
]
};
var chartPlan = document.getElementById('plan');
var planChart = echarts.init(chartPlan);
var optionE;
optionE = {
backgroundColor: "#ffffff",
title: {
top: 0,
left: 0,
text: ''
},
tooltip: {
padding: 6,
formatter: function (obj) {
var value = obj.value;
var tips = '<div style="font-size: 12px;">' + value[0] + '<br>';
tips += '共 ' + value[1] + ' 个工作任务';
tips += '</div>';
return tips;
}
},
visualMap: {
min: 0,
max: 10,
show: false,
inRange: {
color: ['#fafafa', '#20BF3F']
}
},
calendar: {
top: 24,
left: 36,
right: 4,
cellSize: ['auto', 16],
range: ['2022-03-01', '2022-08-01'],
splitLine: {
lineStyle: {
color: '#333',
type: 'dashed',
}
},
itemStyle: {
borderWidth: 0.5
},
yearLabel: { show: false },
monthLabel: {
nameMap: 'cn',
fontSize: 12
},
dayLabel: {
show: true,
formatter: '{start} 1st',
fontWeight: 'lighter',
nameMap: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
fontSize: 12
}
},
series: {
type: 'heatmap',
coordinateSystem: 'calendar',
data: []
}
};
var chartWork = document.getElementById('work');
var workChart = echarts.init(chartWork);
var optionF;
optionF = {
backgroundColor: "#ffffff",
title: {
top: 0,
left: 0,
text: ''
},
tooltip: {
padding: 6,
formatter: function (obj) {
var value = obj.value;
var tips = '<div style="font-size: 12px;">' + value[0] + '<br>';
tips += '共 ' + value[1] + ' 个工时';
tips += '</div>';
return tips;
}
},
visualMap: {
min: 0,
max: 10,
show: false,
inRange: {
color: ['#fafafa', '#359AEF']
}
},
calendar: {
top: 24,
left: 36,
right: 4,
cellSize: ['auto', 16],
range: ['2022-03-01', '2022-08-01'],
splitLine: {
lineStyle: {
color: '#333',
type: 'dashed',
}
},
itemStyle: {
borderWidth: 0.5
},
yearLabel: { show: false },
monthLabel: {
nameMap: 'cn',
fontSize: 12
},
dayLabel: {
show: true,
formatter: '{start} 1st',
fontWeight: 'lighter',
nameMap: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
fontSize: 12
}
},
series: {
type: 'heatmap',
coordinateSystem: 'calendar',
data: []
}
};
function getCalendarData(arr) {
var rangeArray = [];
for (var property in arr) {
rangeArray.push(property);
}
var rangeArray = [rangeArray[0], rangeArray[rangeArray.length - 1]];
var start = +echarts.number.parseDate(rangeArray[0]);
var end = +echarts.number.parseDate(rangeArray[1]);
if (start + 7776000000 > end) {
end = start + 8640000000;
rangeArray[1] = echarts.format.formatTime('yyyy-MM-dd', end);
}
var dayTime = 3600 * 24 * 1000;
var data = [];
for (var time = start; time < end; time += dayTime) {
var this_date = echarts.format.formatTime('yyyy-MM-dd', time);
if (arr[this_date]) {
data.push([this_date, arr[this_date]]);
} else {
data.push([this_date, 0]);
}
}
var res = { 'range': rangeArray, 'data': data };
return res;
}
//燃尽图统计
function cross_count(arr, arr2) {
var planArray = [], doArray = [];
var today = new Date();
var todayStr = today.getFullYear() + '-' + (today.getMonth() + 1) + '-' + today.getDate();
for (var a in arr) {
planArray.push(a);
}
var rangeArray = [planArray[0], planArray[planArray.length - 1]];
if (arr2 instanceof Array == false) {
for (var b in arr2) {
doArray.push(b);
}
if ((+echarts.number.parseDate(doArray[doArray.length - 1])) < (+echarts.number.parseDate(todayStr))) {
doArray.push(todayStr);
}
if ((+echarts.number.parseDate(planArray[planArray.length - 1])) < (+echarts.number.parseDate(doArray[doArray.length - 1]))) {
rangeArray[1] = doArray[doArray.length - 1];
}
}
var start = +echarts.number.parseDate(rangeArray[0]);
var end = +echarts.number.parseDate(rangeArray[1]);
var todayInt = +echarts.number.parseDate(todayStr);
var dayTime = 3600 * 24 * 1000;
var xArray = [], yArray = [], yArray2 = [], done = 0, doneArray = [];
for (var time = start; time <= end; time += dayTime) {
var this_date = echarts.format.formatTime('yyyy-MM-dd', time);
xArray.push(this_date);
var plan = cross_recursion(time, end, arr);
yArray.push(plan);
if (arr2[this_date]) {
done += arr2[this_date];
}
if (time <= todayInt) {
doneArray.push(done);
}
}
for (var i = 0; i < doneArray.length; i++) {
yArray2.push(yArray[0] - doneArray[i]);
}
var start_time = +echarts.number.parseDate(project_start_time), tem_x_array = [], tem_y_array = [];
if (start_time < start) {
for (var tem_time = start_time; tem_time < start; tem_time += dayTime) {
var this_date = echarts.format.formatTime('yyyy-MM-dd', tem_time);
tem_x_array.push(this_date);
tem_y_array.push(yArray[0]);
}
xArray = tem_x_array.concat(xArray);
yArray = tem_y_array.concat(yArray);
yArray2 = tem_y_array.concat(yArray2);
}
return { 'x': xArray, 'y': yArray, 'y2': yArray2 };
}
function cross_recursion(start, end, arr) {
var count = 0;
var dayTime = 3600 * 24 * 1000;
for (var time = start; time <= end; time += dayTime) {
var this_date = echarts.format.formatTime('yyyy-MM-dd', time);
if (arr[this_date]) {
count += arr[this_date];
}
}
return count;
}
//
const moduleInit = ['tool','oaPicker','uploadPlus','tablePlus','oaComment','oaSchedule','oaEdit'];
function gouguInit() {
var tool = layui.tool,tabs = layui.tabs,comment = layui.oaComment;
comment.init({
"box":'commentBox',//容器id
"input": 'commentInput',
"topic_id":project_id,
"module": 'project',
});
$('body').on('click','[data-event="close"]',function(){
tool.ask('确定要关闭该项目?',function(){
let callback = function (e) {
layer.msg(e.msg);
if(e.code==0){
parent.layui.pageTable.reload();
setTimeout(function(){
location.reload();
},2000)
}
}
tool.post("/project/api/close", { 'id': project_id}, callback);
})
})
$('body').on('click','[data-event="open"]',function(){
tool.ask('确定要开启该项目?',function(){
let callback = function (e) {
layer.msg(e.msg);
if(e.code==0){
parent.layui.pageTable.reload();
setTimeout(function(){
location.reload();
},2000)
}
}
tool.post("/project/api/open", { 'id': project_id}, callback);
})
})
overview();
tabs.on('afterChange(projectTab)', function(data){
let index = data.index;
if(index == 1){
project_task();
}
else if(index == 2){
project_schedule();
}
else if(index == 3){
project_document();
}
else if(index == 4){
project_user();
}
else if(index == 5){
project_log();
}
});
}
</script>
{/block}
<!-- /脚本 -->
+6
View File
@@ -0,0 +1,6 @@
<div class="px-3 pt-3 border bg-white">
<div class="comment-input">
<input type="text" id="commentInput" readonly placeholder="发表一下你的看法" class="layui-input" value="">
</div>
<div id="commentBox" class="pt-3"></div>
</div>
+86
View File
@@ -0,0 +1,86 @@
<div class="bg-white">
<table class="layui-hide" id="document" lay-filter="document"></table>
</div>
<script type="text/html" id="toolbardocument">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-sm" lay-event="add">+ 新建文档</button>
</div>
</script>
<script>
function project_document(){
if($('#projectTab').find('li').eq(3).data('load') =='true'){
return false;
}
$('#projectTab').find('li').eq(3).data('load','true');
let tool = layui.tool, table = layui.tablePlus;
//项目任务
parent.layui.documentTable = table.render({
elem: '#document',
title: '文档列表',
toolbar: '#toolbardocument',
cellMinWidth:80,
url: "/project/document/datalist",
where:{'project_id':project_id},
page: true, //开启分页
limit: 20,
cols: [[
{field: 'id', title: '文档编号', width: 80, align: 'center', templet: function (d) {
return 'D' + d.id;
}
}
,{field: 'title', title: '文档主题'}
,{field:'admin_name',title: '创建人', align:'center',width: 80}
,{field:'create_time',title: '创建时间', align:'center', width: 150}
,{
field: 'right',
fixed:'right',
title: '操作',
width: 120,
align: 'center',
templet: function (d) {
var html = '<div class="layui-btn-group">';
var btn1='<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="view">详细</a>'
var btn2='<span class="layui-btn layui-btn-xs" lay-event="edit">编辑</span>';
var btn3='<span class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</span>';
return html+btn1+btn2+btn3+'</div>';
}
}
]]
});
//触发事件
table.on('toolbar(document)', function(obj){
if (obj.event === 'add') {
tool.side('/project/document/add?project_id='+project_id);
return;
}
});
table.on('tool(document)', function(obj){
var data = obj.data; //获得当前行数据
if(obj.event === 'view'){ //查看
let url = '/project/document/view/id/'+data.id;
tool.side(url);
}
if(obj.event === 'edit'){
tool.side('/project/document/add?id='+data.id);
return;
}
if (obj.event === 'del') {
layer.confirm('确定要删除吗?', {
icon: 3,
title: '提示'
}, function(index) {
let callback = function (e) {
layer.msg(e.msg);
if (e.code == 0) {
obj.del();
}
}
tool.delete("/project/document/delete", {id: data.id}, callback);
layer.close(index);
});
}
})
}
</script>
+9
View File
@@ -0,0 +1,9 @@
<div class="border">
<div id="logList" class="log-timeline p-3"></div>
</div>
<script>
function project_log(){
let oaEdit = layui.oaEdit;
oaEdit.log('logList','Project',project_id);
}
</script>
+415
View File
@@ -0,0 +1,415 @@
<table class="layui-table layui-table-form">
<tr>
<td class="layui-td-gray">项目状态</td>
<td><span class="check-status-color-{$detail.status}">『{$detail.status_name}』</span></td>
<td class="layui-td-gray">项目类别</td>
<td>{$detail.cate}</td>
<td class="layui-td-gray">始止日期</td>
<td>{$detail.start_time|date='Y-m-d'} 到 {$detail.end_time|date='Y-m-d'}</td>
<td class="layui-td-gray">所属部门</td>
<td>{$detail.department|default='-'}</td>
</tr>
<tr>
<td class="layui-td-gray">项目经理</td>
<td>{$detail.director_name}</td>
<td class="layui-td-gray">项目成员</td>
<td colspan="5">{$detail.team_admin_names|default='-'}</td>
</tr>
{gt name="$detail.contract_id" value="0"}
<tr>
<td class="layui-td-gray">关联合同</td>
<td colspan="7">{$detail.contract_name}</td>
</tr>
{/gt}
{notempty name="$detail.content"}
<tr>
<td class="layui-td-gray">项目简介</td>
<td colspan="7">{$detail.content}</td>
</tr>
{/notempty}
</table>
<div class="layui-card border" style="margin-top:16px;">
<div class="layui-card-header" style="height:43px; line-height:43px;">
<strong>项目阶段</strong>
</div>
<div class="px-3 py-1 border-b">
<div class="flow-flexbox check-items flow-flex-row">
{volist name="$detail.step_array" id="vo"}
<div class='flow-flexbox flow-flex-row check-item {eq name="$vo.is_current" value="1"} blue{/eq} {notempty name="$step"}{lt name="$vo.sort" value="$step.sort"} gray{/lt}{else/} gray{/notempty}'>
<div class="check-item-icon">
<strong class="iconfont icon-bianji blue"></strong>
<strong class="iconfont icon-kaoheguanli gray"></strong>
<strong class="iconfont icon-genjinjilu black"></strong>
</div>
<div>
<div class="check-item-name"><strong class="f16">{$vo.title}</strong> {$vo.director_name|default=''}</div>
<div class="check-item-time">{$vo.start_time|date='Y-m-d'} 到 {$vo.end_time|date='Y-m-d'}</div>
</div>
<div class="layui-icon layui-icon-right"></div>
</div>
{/volist}
</div>
</div>
{notempty name="$step"}
<div class="p-3 border-b">
<div>
<span class="gray">当前阶段:</span>{$step.title}
<span class="gray" style="margin-left:20px">阶段周期:</span>{$step.start_time|date='Y-m-d'} 到 {$step.end_time|date='Y-m-d'}
<span class="gray" style="margin-left:20px">负责人:</span>{$step.director_name}
{eq name="$step.director_uid" value="$login_admin.id"}
<span class="layui-btn layui-btn-normal layui-btn-xs" data-event="step" data-check="1">确认完成</span>
<span class="layui-btn layui-btn-danger layui-btn-xs" data-event="step" data-check="2">退回上一阶段</span>
{/eq}
</div>
{notempty name="$step.unames"}
<div class="pt-2">
<span class="gray">阶段成员:</span>{$step.unames|default=''}
</div>
{/notempty}
{notempty name="$step.remark"}
<div class="pt-2">
<span class="gray">阶段说明:</span>{$step.remark|default=''}
</div>
{/notempty}
</div>
{/notempty}
<div class="p-3">
<p><strong>阶段流转记录</strong></p>
{notempty name="$step_record"}
<ul class="layui-timeline pt-2">
{volist name="$step_record" id="vo"}
<li class="layui-timeline-item delete_{$vo.delete_time}">
<i class="layui-icon layui-timeline-axis">&#xe63f;</i>
{if ($vo.status == 1)}
<p style="padding-left:24px">{$vo.check_time_str}<span class="black mx-1">{$vo.check_name}</span><span class="mr-1 green">{$vo.status_str}</span>了『{$vo.title}』的工作。操作意见:<span class="green">{$vo.content|default='-'}</span></p>
{else /}
<p style="padding-left:24px">{$vo.check_time_str}<span class="black mx-1">{$vo.check_name}</span>在阶段『{$vo.title}』执行了<span class="mx-1 red">{$vo.status_str}</span>操作。操作意见:<span class="red">{$vo.content|default='-'}</span></p>
{/if}
</li>
{/volist}
</ul>
{else/}
<div class="layui-data-none">暂无阶段流转记录</div>
{/notempty}
</div>
</div>
<div class="layui-card border">
<div class="layui-card-header" style="height:45px; line-height:45px;">
<strong>项目附件</strong>
<button type="button" class="layui-btn layui-btn-xs" id="uploadBtn">上传附件</button>
</div>
<div class="layui-row p-2" id="uploadBox">
{volist name="file_array" id="vo"}
<div class="layui-col-md4" id="fileItem{$vo.id}">{:file_card($vo)}</div>
{/volist}
{empty name="$file_array" }
<div class="layui-data-none">暂无项目附件</div>
{/empty}
</div>
</div>
<div class="layui-row mb-4 border">
<div class="layui-col-xs6 layui-col-md3">
<div class="layui-card">
<div class="layui-card-header" style="height:45px; line-height:45px;">
<strong>项目概况</strong>
</div>
<div class="p-3">
<dl>
<dt>任务 <span class="gray">(已完成/全部)</span></dt>
<dd class="layui-card-value" title="已完成/总任务">{$detail.tasks_finish} / {$detail.tasks}</dd>
</dl>
</div>
<div class="pt-2 px-3">
<dl>
<dt>项目工时 <span class="gray">(实际/计划)</span></dt>
<dd class="layui-card-value" title="实际工时/计划工时">{$detail.hours} / {$detail.plan_hours}</dd>
</dl>
</div>
<div class="pt-2 px-3">
<dl>
<dt>工作记录</dt>
<dd class="layui-card-value" title="工作记录数">{$detail.schedules}</dd>
</dl>
</div>
</div>
</div>
<div class="layui-col-xs6 layui-col-md9">
<div class="layui-card border-l">
<div class="layui-card-header" style="height:45px; line-height:45px;">
<strong>项目进度</strong>
</div>
<div class="layui-card-body">
<div class="layui-row">
<div class="layui-col-md6">
<div id="progress" class="gougu-data-none" style="width:100%; height:200px;"></div>
</div>
<div class="layui-col-md6">
<div id="delay" class="gougu-data-none" style="width:100%; height:200px;"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="layui-card border">
<div class="layui-card-header" style="height:45px; line-height:45px;">
<strong>项目燃尽图</strong>
</div>
<div class="layui-card-body">
<div id="cross" class="gougu-data-none" style="width:100%; height:360px;"
data-tips="任务数:{$detail.tasks},已完成:{$detail.tasks_finish},未完成:{$detail.tasks_unfinish}">
</div>
</div>
</div>
<div class="layui-row border">
<div class="layui-col-xs6 layui-col-md6">
<div class="layui-card border-r">
<div class="layui-card-header" style="height:45px; line-height:45px;">
<strong>任务分配情况</strong>
</div>
<div class="layui-card-body">
<div id="plan" class="gougu-data-none" style="width:100%; height:150px;"></div>
</div>
</div>
</div>
<div class="layui-col-xs6 layui-col-md6">
<div class="layui-card">
<div class="layui-card-header" style="height:45px; line-height:45px;">
<strong>工时登记情况</strong>
</div>
<div class="layui-card-body">
<div id="work" class="gougu-data-none" style="width:100%; height:150px;"></div>
</div>
</div>
</div>
</div>
<script>
function overview(){
let form = layui.form,tool = layui.tool,uploadPlus = layui.uploadPlus;
$('body').on('click','[data-event="step"]',function(){
let check = $(this).data('check');
let callback = function (e) {
layer.msg(e.msg);
if(e.code==0){
setTimeout(function(){
location.reload();
},2000)
parent.layui.pageTable.reload();
}
}
if(check == 2){
$(parent.$('.express-close')).addClass('parent-colse');
layer.open({
type: 1,
title: '请输入退回的原因或理由',
area: ['500px', '252px'],
content: '<div style="padding:5px;"><textarea class="layui-textarea" id="remarkTextarea" style="width: 100%; height:132px;"></textarea></div>',
btnAlign: 'c',
btn: ['确认回退'],
end: function(){
$(parent.$('.express-close')).removeClass('parent-colse');
},
yes: function () {
let remark = $("#remarkTextarea").val();
if (remark != '') {
tool.post("/project/api/step_check", {id: project_id,check:check,content:remark}, callback);
} else {
layer.msg('请输入原因或理由');
}
}
})
}
else{
$(parent.$('.express-close')).addClass('parent-colse');
layer.open({
type: 1,
title: '请输入操作意见',
area: ['500px', '252px'],
content: '<div style="padding:5px;"><textarea class="layui-textarea" id="remarkTextarea" style="width: 100%; height:132px;"></textarea></div>',
btnAlign: 'c',
btn: ['确认完成'],
end: function(){
$(parent.$('.express-close')).removeClass('parent-colse');
},
yes: function () {
let remark = $("#remarkTextarea").val();
if (remark != '') {
tool.post("/project/api/step_check", {id: project_id,check:check,content:remark}, callback);
} else {
layer.msg('请输入操作意见');
}
}
})
}
});
var attachment = new uploadPlus({
"target":'uploadBtn',
"targetBox":'uploadBox',
"attachment":{
"type":1,//0ajax多文件模式,1ajax单文件单记录模式
"uidDelete":true,//是否开启只有上传人自己才能删除自己的附件
"ajaxSave":function(res){
let callback = function (e) {
layer.msg('上传成功');
setTimeout(function(){
location.reload();
},2000)
}
tool.post("/project/api/add_file", { 'topic_id': project_id, 'file_id': res.data.id, 'file_name': res.data.name, 'module': 'project' }, callback);
},
"ajaxDelete":function(file_id){
let callback = function (e) {
layer.msg(e.msg);
if (e.code == 0) {
$('#fileItem' + file_id).remove();
}
}
tool.delete("/project/api/delete_file", {id: file_id}, callback);
}
}
});
let callback = function (res) {
if (res.data.date_tasks instanceof Array == false) {
optionA.title.text = res.data.task_pie.ok_lv + '%';
optionA.series = [
{
type: 'pie',
radius: ['60%', '80%'],
center: ['50%', '50%'],
avoidLabelOverlap: false,
label: {
show: false
},
data: [
{ value: res.data.task_pie.count - res.data.task_pie.count_ok, name: '待处理' },
{ value: res.data.task_pie.count_ok, name: '已完成' }
]
}
];
optionA && progressChart.setOption(optionA);
optionB.title.text = res.data.task_pie.delay_lv + '%';
optionB.series = [
{
type: 'pie',
radius: ['60%', '80%'],
center: ['50%', '50%'],
avoidLabelOverlap: false,
label: {
show: false
},
data: [{
value: res.data.task_pie.delay,
name: '延迟',
itemStyle: {
color: "#ED6666",
}
},
{
value: (res.data.task_pie.count - res.data.task_pie.delay),
name: '未延迟',
itemStyle: {
color: "#91CC75",
}
}
]
}
];
optionB && delayChart.setOption(optionB);
var dataD = cross_count(res.data.date_tasks, res.data.date_tasks_ok);
var tips = $('#cross').data('tips');
optionD.title = {
text: '',
subtext: tips,
top: -10,
},
optionD.xAxis = {
type: 'category',
boundaryGap: false,
splitLine: {
show: true,
lineStyle: {
type: 'dashed'
}
},
data: dataD.x,
axisLabel: {
rotate: 30,
formatter: function (value, index) {
return value.slice(5);
}
}
};
optionD.series = [
{
name: '任务计划剩余',
type: 'line',
showSymbol: false,
markLine: {
data: [{ type: 'average', name: 'Avg' }],
},
lineStyle: {
width: 2
},
data: dataD.y
},
{
name: '任务实际剩余',
type: 'line',
showSymbol: false,
areaStyle: {
opacity: 0.1
},
markLine: {
data: [{ type: 'average', name: 'Avg' }],
},
lineStyle: {
width: 2
},
data: dataD.y2
}
]
optionD && crossChart.setOption(optionD)
var dataE = getCalendarData(res.data.date_tasks);
optionE.calendar.range = dataE.range,
optionE.series = {
type: 'heatmap',
coordinateSystem: 'calendar',
data: dataE.data
}
optionE && planChart.setOption(optionE);
if (res.data.date_schedules instanceof Array == false) {
var dataF = getCalendarData(res.data.date_schedules);
optionF.calendar.range = dataF.range,
optionF.series = {
type: 'heatmap',
coordinateSystem: 'calendar',
data: dataF.data
}
optionF && workChart.setOption(optionF);
}
}
}
tool.get('/project/api/get_chart_data', { 'project_id': project_id }, callback);
window.onresize = function () {
progressChart.resize();
delayChart.resize();
crossChart.resize();
planChart.resize();
workChart.resize();
}
}
</script>
+64
View File
@@ -0,0 +1,64 @@
<div class="bg-white">
<table class="layui-hide" id="scheduleApi" lay-filter="schedule"></table>
</div>
<script type="text/html" id="toolbarschedule">
<div class="layui-btn-container">
<span class="yellow">如需创建工作日志请到项目任务的详情页创建</span>
</div>
</script>
<script>
function project_schedule(){
if($('#projectTab').find('li').eq(2).data('load') =='true'){
return false;
}
$('#projectTab').find('li').eq(2).data('load','true');
let form = layui.form,tool = layui.tool,table = layui.tablePlus,work = layui.oaSchedule,oaPicker = layui.oaPicker;
//项目工作记录
layui.scheduleTable = table.render({
elem: '#scheduleApi',
title: '工作记录列表',
toolbar: '#toolbarschedule',
cellMinWidth: 80,
url: "/oa/api/get_schedule", //数据接口
where: { 'project_id': project_id },
cols: [[ //表头
{ field: 'id', title: '序号', width: 60, align: 'center' }
, {
field: 'start_time', title: '工作时间范围', align: 'center', width: 180, templet: function (d) {
var html = d.start_time + '至' + d.end_time;
return html;
}
}
, { field: 'labor_time', title: '工时', style: 'color: #91CC75;', align: 'center', width: 60 }
, { field: 'labor_type_string', title: '工作类型', align: 'center', width: 90,templet:function(d){
var html = '<span class="layui-color-'+d.labor_type+'">'+d.labor_type_string+'</span>';
return html;
}}
, { field: 'work_cate', title: '工作类别', align: 'center', width: 100 }
, { field: 'title', title: '工作内容' }
, { field: 'name', title: '执行员工', align: 'center', width: 80 }
, { field: 'department', title: '所在部门', align: 'center', width: 100 }
, { field: 'create_time', title: '记录时间', align: 'center', width: 150 }
, {title: '操作',fixed:'right', align: 'center', width: 100, templet: function (d) {
if(d.admin_id == login_admin){
return '<div class="layui-btn-group"><span class="layui-btn layui-btn-xs" lay-event="edit">修改</span><span class="layui-btn layui-btn-normal layui-btn-xs" lay-event="view">详细</span></div>';
}
else{
return '<span class="layui-btn layui-btn-normal layui-btn-xs" lay-event="view">详细</span>';
}
}
}
]]
});
//监听行工具事件
table.on('tool(schedule)', function (obj) {
if (obj.event === 'edit') {
work.add(0, obj.data);
}
if (obj.event === 'view') {
work.view(obj.data);
}
return false;
})
}
</script>
+64
View File
@@ -0,0 +1,64 @@
<div class="bg-white">
<table class="layui-hide" id="task" lay-filter="task"></table>
</div>
<script type="text/html" id="toolbartask">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-sm" lay-event="add">+ 新建任务</button>
</div>
</script>
<script>
function project_task(){
if($('#projectTab').find('li').eq(1).data('load') =='true'){
return false;
}
$('#projectTab').find('li').eq(1).data('load','true');
let tool = layui.tool, table = layui.tablePlus;
//项目任务
parent.layui.taskTable = table.render({
elem: '#task',
title: '任务列表',
toolbar: '#toolbartask',
cellMinWidth:80,
url: "/project/api/get_task",
where:{'project_id':project_id},
page: true, //开启分页
limit: 20,
cols: [[
{field: 'id', title: '任务编号', width: 80, align: 'center', fixed: 'left', templet: function (d) {
return 'T' + d.id;
}
}
,{ field: 'status', title: '任务状态', align: 'center', width: 90, templet: function (d) {
var html = '<span class="check-status-color-' + d.status + '">『' + d.status_name + '』</span>';
return html;
}
}
, { field: 'cate_name', title: '工作类型', width: 90, align: 'center'}
,{field:'title',title: '任务主题', minWidth: 300,templet:function(d){
var html = '<span class="layui-badge layui-bg-'+d.priority+'">'+d.priority_name+'</span> <a data-href="/project/task/view/id/'+d.id+'.html" class="side-a">'+d.title+'</a>';
return html;
}}
,{field:'director_name',title: '负责人', align:'center',width: 80}
,{field:'assist_admin_names',title: '协作人',width: 160}
,{field:'plan_hours',title: '预估工时', align:'center',width: 80}
,{field:'end_time',title: '预计结束日期', width: 150,templet:function(d){
var html = d.end_time;
if(d.delay>0){
html+= '<span class="red ml-1" style="font-size:12px;">逾期'+d.delay+'天</span>';
}
return html;
}}
]]
});
//触发事件
table.on('toolbar(task)', function(obj){
var checkStatus = table.checkStatus(obj.config.id);
switch(obj.event){
case 'add':
tool.side('/project/task/add?project_id='+project_id);
break;
};
});
}
</script>
+116
View File
@@ -0,0 +1,116 @@
<div class="bg-white">
<table class="layui-hide" id="user" lay-filter="user"></table>
</div>
<script type="text/html" id="toolbaruser">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-sm" lay-event="add">+ 新增项目成员</button>
</div>
</script>
<script>
function project_user(){
if($('#projectTab').find('li').eq(4).data('load') =='true'){
return false;
}
$('#projectTab').find('li').eq(4).data('load','true');
let tool = layui.tool, table = layui.tablePlus,oaPicker = layui.oaPicker;
//项目成员
layui.userTable = table.render({
elem: '#user',
title: '项目成员列表',
cellMinWidth: 60,
toolbar: '#toolbaruser',
url: "/project/api/project_user", //数据接口
where: { 'tid': project_id },
page: false, //开启分页
limit: 20,
cols: [[ //表头
{ field: 'name', fixed: 'left', title: '成员姓名', width: 90, align: 'center', rowspan: 2 }
, { field: 'create_time', title: '进入项目日期', width: 110, align: 'center', rowspan: 2 }
, { field: 'role', title: '角色', align: 'center', width: 90, rowspan: 2, templet: function (d) {
var html='<span style="color: #4285F4;">普通成员</span>';
if(d.role==1){
html='<span style="color: #EE6666;">项目创建人</span>';
}
if(d.role==2){
html='<span style="color: #91CC75;">项目负责人</span>';
}
return html;
}}
, { field: 'position', title: '职位', align: 'center', width: 100, rowspan: 2 }
, { field: 'department', title: '所在部门', align: 'center', width: 120, rowspan: 2 }
, { field: 'mobile', title: '手机号码', align: 'center', width: 110, rowspan: 2 }
, { align: 'center', title: '工作记录', colspan: 2 }
, { align: 'center', title: '项目任务', colspan: 3 }
, { field: 'delete_time', title: '移除日期', align: 'center', minWidth: 120, rowspan: 2 }
, { field: 'status',fixed: 'right', title: '状态', align: 'center', width: 60, rowspan: 2, templet: function (d) {
var html = '<span style="color:#EE6666">✘</span>';
if(d.delete_time == '-')
html = '<span style="color:#91CC75">✔</span>';
return html;
}}
, {title: '操作',fixed: 'right', align: 'center', width: 60, rowspan: 2, templet: function (d) {
var html = '<span class="layui-btn layui-btn-xs" lay-event="recover">恢复</span>';
if(d.delete_time == '-')
html = '<span class="layui-btn layui-btn-danger layui-btn-xs" lay-event="remove">移除</span>';
return html;
}
}
], [
{ field: 'schedules', align: 'center', style: 'color: #91CC75;', width: 72, 'title': '记录' }
, { field: 'labor_times', align: 'center', style: 'color: #4285F4;', width: 70, 'title': '工时' }
, { field: 'tasks_unfinish', align: 'center', style: 'color: #91CC75;', width: 72, 'title': '进行中' }
, { field: 'tasks_finish', align: 'center', style: 'color: #FAC858;', width: 70, 'title': '已完成' }
, { field: 'tasks_pensent', align: 'center', style: 'color: #EE6666;', width: 72, 'title': '完成率' }
]]
});
//触发事件
table.on('toolbar(user)', function(obj){
var checkStatus = table.checkStatus(obj.config.id);
switch(obj.event){
case 'add':
oaPicker.employeeInit({
type: 1,
callback: function (data) {
let callback = function (e) {
layer.msg(e.msg);
if(e.code == 0){
layui.userTable.reload();
}
}
let select_id=[];
for(var a=0; a<data.length;a++){
select_id.push(data[a].id);
}
let ids = select_id.join(',');
tool.post("/project/api/add_user", {uid: ids,project_id: project_id}, callback);
}
})
break;
};
});
//监听行工具事件
table.on('tool(user)', function (obj) {
let postData = { "id": obj.data.id };
let callback = function (e) {
layer.closeAll();
layer.msg(e.msg);
if(e.code == 0){
layui.userTable.reload();
}
}
if (obj.event === 'remove') {
tool.ask('确定要移除该项目成员吗?', function (index) {
tool.delete("/project/api/remove_user", postData, callback);
});
}
if (obj.event === 'recover') {
tool.ask('确定要恢复该项目成员吗?', function (index) {
tool.post("/project/api/recover_user", postData, callback);
});
}
return;
});
}
</script>
+45
View File
@@ -0,0 +1,45 @@
{extend name="../../base/view/common/base" /}
<!-- 主体 -->
{block name="body"}
<form class="layui-form p-page">
<h3 class="pb-3">项目步骤</h3>
<table class="layui-table layui-table-form">
<tr>
<td class="layui-td-gray">项目步骤名称<font>*</font></td>
<td><input type="text" name="title" lay-verify="required" autocomplete="off" placeholder="请输入项目步骤名称" lay-reqText="请输入项目步骤名称"class="layui-input" value="{$detail.title|default=''}"></td>
<td class="layui-td-gray">排序</td>
<td><input type="text" name="sort" placeholder="请输入排序,数字" autocomplete="off" class="layui-input" value="{$detail.sort|default=''}"></td>
</tr>
</table>
<div class="pt-4">
<input type="hidden" name="id" value="{$detail.id|default=0}"/>
<button class="layui-btn layui-btn-normal" lay-submit="" lay-filter="webform">立即提交</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</form>
{/block}
<!-- /主体 -->
<!-- 脚本 -->
{block name="script"}
<script>
var moduleInit = ['tool'];
function gouguInit() {
var form = layui.form, tool = layui.tool;
//监听提交
form.on('submit(webform)', function (data) {
let callback = function (e) {
layer.msg(e.msg);
if (e.code == 0) {
tool.sideClose(1000);
}
}
let clickbtn = $(this);
tool.post("/project/step/add", data.field, callback,clickbtn);
return false;
});
}
</script>
{/block}
<!-- /脚本 -->
+124
View File
@@ -0,0 +1,124 @@
{extend name="../../base/view/common/base" /}
<!-- 主体 -->
{block name="body"}
<div class="p-page">
<table class="layui-hide" id="table_step" lay-filter="table_step"></table>
</div>
<script type="text/html" id="toolbarDemo">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-sm add-new" type="button">+ 添加项目步骤</button>
</div>
</script>
{/block}
<!-- /主体 -->
<!-- 脚本 -->
{block name="script"}
<script>
const moduleInit = ['tool'];
function gouguInit() {
var table = layui.table, tool = layui.tool;
layui.pageTable = table.render({
elem: "#table_step"
,toolbar: '#toolbarDemo'
,url: "/project/step/datalist"
,page: false
,limit: 999
,cellMinWidth: 80
,cols: [[
{field:'id',width:80, title: 'ID号', align:'center'}
,{field:'title',title: '类型名称'}
,{field:'status', title: '状态',width:80,align:'center',templet: function(d){
var html1='<span class="green">正常</span>';
var html2='<span class="yellow">禁用</span>';
if(d.status==1){
return html1;
}
else{
return html2;
}
}}
,{width:100,title: '操作', align:'center',ignoreExport:true,templet: function(d){
var html='';
var btn='<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="edit">编辑</a>';
var btn1='<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="disable">禁用</a>';
var btn2='<a class="layui-btn layui-btn-xs" lay-event="open">启用</a>';
if(d.status==1){
html = '<div class="layui-btn-group">'+btn+btn1+'</div>';
}
else{
html = '<div class="layui-btn-group">'+btn+btn2+'</div>';
}
return html;
}}
]]
});
table.on('tool(table_step)',function (obj) {
if(obj.event === 'edit'){
add_cate(obj.data.id,obj.data.title);
}
if(obj.event === 'disable'){
layer.confirm('确定要禁用该记录吗?', {icon: 3, title:'提示'}, function(index){
let callback = function (e) {
layer.msg(e.msg);
if (e.code == 0) {
layui.pageTable.reload();
}
}
tool.post("/project/step/set", {id:obj.data.id,status: 0}, callback);
layer.close(index);
});
}
if(obj.event === 'open'){
layer.confirm('确定要启用该记录吗?', {icon: 3, title:'提示'}, function(index){
let callback = function (e) {
layer.msg(e.msg);
if (e.code == 0) {
layui.pageTable.reload();
}
}
tool.post("/project/step/set", {id:obj.data.id,status:1}, callback);
layer.close(index);
});
}
});
$('body').on('click','.add-new',function(){
add_cate(0,'');
});
function add_cate(id,val){
var title = '新增类型';
if(id>0){
title = '编辑类型';
}
layer.prompt({
title: title,
value: val,
yes: function(index, layero) {
// 获取文本框输入的值
var value = layero.find(".layui-layer-input").val();
if (value) {
let callback = function (e) {
layer.msg(e.msg);
if (e.code == 0) {
layui.pageTable.reload();
}
}
tool.post("/project/step/add", {id: id,title: value}, callback);
layer.close(index);
} else {
layer.msg('请填写类型名称');
}
}
})
}
}
</script>
{/block}
<!-- /脚本 -->
+16
View File
@@ -0,0 +1,16 @@
{extend name="../../base/view/common/base" /}
<!-- 主体 -->
{block name="body"}
<div class="p-page">
<h3 class="pb-3">项目步骤详情</h3>
<table class="layui-table layui-table-form">
<tr>
<td class="layui-td-gray">项目步骤名称</td>
<td>{$detail.title|default=''}"></td>
<td class="layui-td-gray">排序</td>
<td>{$detail.sort|default=0}</td>
</tr>
</table>
</div>
{/block}
<!-- /主体 -->
+111
View File
@@ -0,0 +1,111 @@
{extend name="../../base/view/common/base" /}
{block name="body"}
<form class="layui-form p-page">
<h3 class="pb-3">新建任务</h3>
<table class="layui-table layui-table-form">
<tr>
<td class="layui-td-gray">任务主题<font>*</font></td>
<td colspan="3"><input type="text" name="title" lay-verify="required" lay-reqText="请输入任务主题" placeholder="请输入任务主题" class="layui-input" value=""></td>
<td class="layui-td-gray-2">任务优先级<span style="color: red">*</span></td>
<td>
<select name="priority" lay-verify="required" lay-reqText="请选择优先级">
{volist name=":get_priority()" id="vo"}
{gt name="$key" value="0"}
<option value="{$key}">{$vo}</option>
{/gt}
{/volist}
</select>
</td>
</tr>
<tr>
<td class="layui-td-gray">工作类型<span style="color: red">*</span></td>
<td>
<select name="work_id" lay-verify="required" lay-reqText="请选择工作类型">
<option value="">请选择</option>
{volist name=":get_base_data('WorkCate')" id="vo"}
<option value="{$vo.id}">{$vo.title}</option>
{/volist}
</select>
</td>
<td class="layui-td-gray">预估工时<span style="color: red">*</span></td>
<td><input type="text" name="plan_hours" lay-verify="required|number" lay-reqText="请填写预估工时" placeholder="请填写预估工时" class="layui-input" value=""></td>
<td class="layui-td-gray-2">预计结束日期<span style="color: red">*</span></td>
<td><input type="text" name="end_time" readonly lay-verify="required" lay-reqText="请选择预计结束日期" placeholder="请选择时间" class="layui-input tool-time" value=""></td>
</tr>
<tr>
<td class="layui-td-gray">负责人</td>
<td>
<input type="text" name="director_name" placeholder="请选择" readonly class="layui-input picker-admin" value="">
<input type="hidden" name="director_uid" readonly value="0">
</td>
{empty name="$project_id"}
<td class="layui-td-gray">协作人</td>
<td>
<input type="text" name="assist_admin_names" placeholder="请选择" readonly class="layui-input picker-admin" data-type="2" value="">
<input type="hidden" name="assist_admin_ids" readonly value="">
</td>
<td class="layui-td-gray">关联项目</td>
<td>
<input type="text" name="project_name" placeholder="所属项目" readonly class="layui-input picker-oa" data-types="project" autocomplete="off" />
<input type="hidden" name="project_id" value="0" />
</td>
{else/}
<td class="layui-td-gray">协作人</td>
<td colspan="3">
<input type="hidden" name="project_id" readonly value="{$project_id}">
<input type="text" name="assist_admin_names" placeholder="请选择" readonly class="layui-input picker-admin" data-type="2" value="">
<input type="hidden" name="assist_admin_ids" readonly value="">
</td>
{/empty}
</tr>
<tr>
<td class="layui-td-gray" style="vertical-align:top;">详细描述<font>*</font></td>
<td colspan="5">
<textarea name="content" placeholder="请输入内容" class="layui-textarea" id="container" style="border:0;padding:0"></textarea>
</td>
</tr>
</table>
<div class="pt-4">
<input type="hidden" name="id" value="0" />
<button class="layui-btn layui-btn-normal" lay-submit="" lay-filter="webform">立即提交</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</form>
{/block}
<!-- /主体 -->
<!-- 脚本 -->
{block name="script"}
<script>
const moduleInit = ['tool','tinymce','oaPicker'];
function gouguInit() {
var form = layui.form,tool=layui.tool, oaPicker = layui.oaPicker;
//编辑器初始化
var editor = layui.tinymce;
var edit = editor.render({
selector: "#container",
images_upload_url: '/api/index/upload/sourse/tinymce',//图片上传接口
height: 500
});
//监听提交
form.on('submit(webform)', function (data) {
data.field.content = tinyMCE.editors['container'].getContent();
if (data.field.content == '') {
layer.msg('请先完善任务描述内容');
return false;
}
let callback = function (e) {
layer.msg(e.msg);
if (e.code == 0) {
tool.sideClose(1000);
}
}
let clickbtn = $(this);
tool.post("/project/task/add", data.field, callback,clickbtn);
return false;
});
}
</script>
{/block}
+111
View File
@@ -0,0 +1,111 @@
{extend name="../../base/view/common/base" /}
{block name="style"}
<style>
.layui-unselect dl {max-height:188px;}
</style>
{/block}
<!-- 主体 -->
{block name="body"}
<div class="p-page">
<form class="layui-form gg-form-bar border-x border-t" lay-filter="barsearchform">
<div class="layui-input-inline" style="width:100px;">
<input type="text" name="username" placeholder="请选择评论人" class="layui-input picker-admin" readonly />
<input type="text" name="uid" value="" style="display:none" />
</div>
<div class="layui-input-inline" style="width:300px;">
<input type="text" name="keywords" placeholder="关键字,输入评论内容" class="layui-input"/>
</div>
<div class="layui-input-inline" style="width:150px;">
<button class="layui-btn layui-btn-normal" lay-submit="" lay-filter="table-search"><i class="layui-icon layui-icon-search mr-1"></i>搜索</button>
<button type="reset" class="layui-btn layui-btn-reset" lay-filter="table-reset">清空</button>
</div>
</form>
<div>
<table class="layui-hide" id="test" lay-filter="test"></table>
</div>
</div>
<script type="text/html" id="toolbarDemo">
<h3>任务评论</h3>
</script>
{/block}
<!-- /主体 -->
<!-- 脚本 -->
{block name="script"}
<script>
const moduleInit = ['tool','tablePlus','oaPicker'];
function gouguInit() {
var form = layui.form,table = layui.tablePlus,tool=layui.tool, work = layui.oaSchedule;
layui.commentTable = table.render({
elem: '#test'
,toolbar: '#toolbarDemo'
,title:'评论列表'
,url: "/project/task/comment"
,cellMinWidth: 80
,is_excel:true
,height: 'full-110'
,cols: [[ //表头
{field: 'id', title: '序号',fixed: 'left', width:80, align:'center'}
,{field: 'content', title: '评论内容'}
,{field: 'project', title: '关联项目&任务', width:300,templet:function(d){
var html='';
if(d.labor_type==0){
html='<span class="layui-color-'+d.labor_type+'">-</span>';
}
else if(d.module =='project'){
html='<span class="green">『项目』</span><a data-href="/project/index/view/id/'+d.topic_id+'.html" class="side-a">'+d.topic_name+'</a>';
}
else{
html='<span class="blue">『任务』</span><a data-href="/project/task/view/id/'+d.topic_id+'.html" class="side-a">'+d.topic_name+'</a>';
}
return html;
}}
,{field: 'to_names', title: '@关联人', width:160}
,{field: 'admin_name', title: '评论人', align:'center',width:80}
,{field: 'create_time', title: '评论时间', align:'center',width:150}
,{field: 'right', title: '操作',fixed:'right', width:90, align:'center',templet:function(d){
if(d.read==0){
return '<button class="layui-btn layui-btn-xs layui-btn-normal" lay-event="read">设为已读</button>';
}
else if(d.read==2){
return '<button class="layui-btn layui-btn-xs layui-btn-danger" lay-event="del">删除</button>';
}
else{
return '-';
}
}}
]]
});
table.on('tool(test)', function(obj){
var data = obj.data;
if (obj.event === 'read') {
let callback = function (e) {
layer.msg(e.msg);
if (e.code == 0) {
layui.commentTable.reload();
}
}
tool.post("/project/api/read_comment", { id: data.id }, callback);
}
if (obj.event === 'del') {
layer.confirm('确定要删除该评论吗?', { icon: 3, title: '提示' }, function (index) {
let callback = function (e) {
layer.msg(e.msg);
if (e.code == 0) {
obj.del();
}
}
tool.delete("/project/api/delete_comment", { id: data.id }, callback);
layer.close(index);
});
return;
}
return;
});
}
</script>
{/block}
<!-- /脚本 -->
+146
View File
@@ -0,0 +1,146 @@
{extend name="../../base/view/common/base" /}
<!-- 主体 -->
{block name="style"}
<style>
.layui-unselect dl {
max-height: 188px;
}
</style>
{/block}
{block name="body"}
<div class="p-page">
<div class="gg-form-bar border-t border-x">
<form id="taskForm" class="layui-form" lay-filter="barsearchform">
<div class="layui-input-inline" style="width:100px;">
<select name="status" lay-filter="status">
<option value="">任务状态</option>
{volist name=":get_task_status()" id="vo"}
{gt name="$key" value="0"}
<option value="{$key}">{$vo}</option>
{/gt}
{/volist}
</select>
</div>
<div class="layui-input-inline" style="width:100px;">
<select name="priority" lay-filter="priority">
<option value="">优先级</option>
{volist name=":get_priority()" id="vo"}
{gt name="$key" value="0"}
<option value="{$key}">{$vo}</option>
{/gt}
{/volist}
</select>
</div>
<div class="layui-input-inline" style="width:100px;">
<select name="work_id" lay-filter="cate">
<option value="">工作类型</option>
{volist name=":get_base_data('WorkCate')" id="vo"}
<option value="{$vo.id}">{$vo.title}</option>
{/volist}
</select>
</div>
<div class="layui-input-inline" style="width:240px;">
<input type="text" name="project_name" placeholder="所属项目" readonly class="layui-input picker-oa" data-types="project" autocomplete="off" />
<input name="project_id" style="display: none;" value="" />
</div>
<div class="layui-input-inline" style="width:160px;">
<input type="text" name="director" placeholder="任务负责人,可多选" readonly class="layui-input picker-admin" data-type="2" autocomplete="off" />
<input name="director_uid" style="display: none;" value="" />
</div>
<div class="layui-input-inline" style="width:240px;">
<input type="text" name="keywords" placeholder="关键字,任务主题/描述内容" class="layui-input" autocomplete="off" />
</div>
<div class="layui-input-inline" style="width:150px;">
<button class="layui-btn layui-btn-normal" lay-submit="" lay-filter="table-search"><i class="layui-icon layui-icon-search mr-1"></i>搜索</button>
<button type="reset" class="layui-btn layui-btn-reset" lay-filter="table-reset">清空</button>
</div>
</form>
</div>
<table class="layui-hide" id="task" lay-filter="task"></table>
</div>
<script type="text/html" id="toolbarDemo">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-sm" lay-event="add">+ 新建任务</button>
</div>
</script>
{/block}
<!-- /主体 -->
<!-- 脚本 -->
{block name="script"}
<script>
const moduleInit = ['tool','tablePlus','oaPicker'];
function gouguInit() {
var table = layui.tablePlus, form = layui.form, tool = layui.tool;
layui.taskTable = table.render({
elem: '#task',
title: '任务列表',
toolbar: '#toolbarDemo',
is_excel:true,
cellMinWidth: 80,
url: "/project/task/datalist", //数据接口
height: 'full-114',
cols: [[
{field: 'id', title: '任务编号', width: 80, align: 'center', fixed: 'left', templet: function (d) {
return 'T' + d.id;
}}
,{ field: 'status', title: '任务状态', align: 'center', width: 90, templet: function (d) {
var html = '<span class="check-status-color-' + d.status + '">『' + d.status_name + '』</span>';
return html;
}
}
, { field: 'cate_name', title: '工作类型', width: 90, align: 'center'}
,{field:'title',title: '任务主题', minWidth: 300,templet:function(d){
var html = '<span class="layui-badge layui-bg-'+d.priority+'">'+d.priority_name+'</span> <a data-href="/project/task/view/id/'+d.id+'.html" class="side-a">'+d.title+'</a>';
return html;
}}
,{field:'director_name',title: '负责人', align:'center',width: 80}
,{field:'assist_admin_names',title: '协作人',width: 160}
,{field:'plan_hours',title: '预估工时', align:'center',width: 80}
,{field:'end_time',title: '预计结束日期', width: 150,templet:function(d){
var html = d.end_time;
if(d.delay>0){
html+= '<span class="red ml-1" style="font-size:12px;">逾期'+d.delay+'天</span>';
}
return html;
}}
,{field: 'right', title: '操作',fixed:'right', width:90, align:'center',templet:function(d){
var html='<div class="layui-btn-group"><button class="layui-btn layui-btn-xs layui-btn-normal" lay-event="view">查看</button><button class="layui-btn layui-btn-xs layui-btn-danger" lay-event="del">删除</button></div>';
return html;
}}
]]
});
//触发事件
table.on('toolbar(task)', function(obj){
if(obj.event === 'add'){
tool.side('/project/task/add');
return;
}
});
table.on('tool(task)', function(obj){
if(obj.event === 'view'){
tool.side('/project/task/view/id/'+obj.data.id);
return;
}
if(obj.event === 'del'){
layer.confirm('您确定要删除该任务?', {
icon: 3,
title: '提示'
}, function (index) {
let callback = function (e) {
layer.msg(e.msg);
if (e.code == 0) {
layui.taskTable.reload();
}
}
tool.delete("/project/task/del", { id: obj.data.id }, callback);
layer.close(index);
});
}
});
}
</script>
{/block}
<!-- /脚本 -->
+234
View File
@@ -0,0 +1,234 @@
{extend name="../../base/view/common/base" /}
{block name="style"}
<style>
.layui-unselect dl {max-height:188px;}
</style>
{/block}
<!-- 主体 -->
{block name="body"}
<div class="p-page">
<form class="layui-form gg-form-bar border-x border-t" lay-filter="barsearchform">
<div class="layui-input-inline" style="width:180px;">
<input type="text" class="layui-input tool-time" data-range="至" placeholder="选择时间区间" readonly name="range_time">
</div>
<div class="layui-input-inline" style="width:100px;">
<input type="text" name="username" placeholder="请选择员工" class="layui-input picker-admin" readonly />
<input type="text" name="uid" value="" style="display:none" />
</div>
<div class="layui-input-inline" style="width:300px;">
<input type="text" name="keywords" placeholder="关键字,输入工作内容" class="layui-input"/>
</div>
<div class="layui-input-inline" style="width:150px;">
<button class="layui-btn layui-btn-normal" lay-submit="" lay-filter="table-search"><i class="layui-icon layui-icon-search mr-1"></i>搜索</button>
<button type="reset" class="layui-btn layui-btn-reset" lay-filter="table-reset">清空</button>
</div>
</form>
<div>
<table class="layui-hide" id="test" lay-filter="test"></table>
</div>
</div>
<script type="text/html" id="toolbarDemo">
<h3>任务工时</h3>
</script>
{/block}
<!-- /主体 -->
<!-- 脚本 -->
{block name="script"}
<script>
const is_leader = '{$is_leader}';
const role_auth = '{$role_auth}';
const hour_auth = '{$hour_auth}';
const moduleInit = ['tool','tablePlus','oaPicker','oaSchedule'];
function gouguInit() {
var form = layui.form,table = layui.tablePlus,tool=layui.tool, work = layui.oaSchedule,laydate = layui.laydate;
layui.scheduleTable = table.render({
elem: '#test'
,toolbar: '#toolbarDemo'
,title:'工时列表'
,url: "/project/task/hour"
,cellMinWidth: 80
,is_excel:true
,height: 'full-114'
,cols: [[ //表头
{field: 'id', title: '序号',fixed: 'left', width:80, align:'center'}
,{field: 'labor_type_string', title: '工作类型', align:'center',width:90,templet:function(d){
var html='';
if(d.labor_type==0){
html='<span class="layui-color-'+d.labor_type+'">-</span>';
}
else if(d.labor_type == 1){
html='<span class="layui-color-'+d.labor_type+'">案头工作</span>';
}
else if(d.labor_type == 2){
html='<span class="layui-color-'+d.labor_type+'">外勤工作</span>';
}
return html;
}}
,{field: 'work_cate', title: '工作类别', align:'center',width:80}
,{field: 'start_time', title: '工作时间范围', align:'center',width:186,templet:function(d){
var html=d.start_time+'至'+d.end_time;
return html;
}}
,{field: 'name', title: '执行人', align:'center',width:80}
,{field: 'labor_time', title: '工时', align:'center',width:80}
,{field: 'title', title: '工作内容'}
,{field: 'task', title: '关联任务', width:200}
,{field: 'project', title: '关联项目', width:180}
,{field: 'create_time', title: '记录时间', align:'center',width:150}
,{field: 'right', title: '操作',fixed:'right', width:180, align:'center',templet:function(d){
var html='<div class="layui-btn-group">';
if(hour_auth==2 && d.admin_id==login_admin){
//开放员工自己调整
html+='<button class="layui-btn layui-btn-xs layui-bg-orange" lay-event="time">调整工时</button>';
html+='<button class="layui-btn layui-btn-xs layui-btn-danger" lay-event="del">删除</button>';
}
if(hour_auth==3 && is_leader > 0){
//开放部门负责人调整
html+='<button class="layui-btn layui-btn-xs layui-bg-orange" lay-event="time">调整工时</button>';
html+='<button class="layui-btn layui-btn-xs layui-btn-danger" lay-event="del">删除</button>';
}
if(hour_auth==4 && role_auth > 0){
//开放日常办公数据管理员调整
html+='<button class="layui-btn layui-btn-xs layui-bg-orange" lay-event="time">调整工时</button>';
html+='<button class="layui-btn layui-btn-xs layui-btn-danger" lay-event="del">删除</button>';
}
if(d.admin_id==login_admin){
html+='<button class="layui-btn layui-btn-xs" lay-event="edit">编辑</button>';
}
html+='<button class="layui-btn layui-btn-xs layui-btn-normal" lay-event="view">查看</button>';
html+='</div>';
return html;
}}
]]
});
//更改工时
table.on('tool(test)', function(obj){
var data = obj.data;
if(obj.event === 'time'){
var content='<form class="layui-form" style="width:568px">\
<table class="layui-table" style="margin:15px 15px 0;">\
<tr>\
<td class="layui-td-gray-2">工作时间范围<font>*</font></td>\
<td>\
<input id="start_time_a" name="start_time_a" style="width:120px; display:inline-block;" autocomplete="off" class="layui-input" value="" readonly lay-verify="required" lay-reqText="请选择"><div style="display: inline-block; margin-left:5px; width: 80px;"><select lay-filter="start_time_b" id="start_time_b"></select></div> 至 <input id="end_time_a" name="end_time_a" style="width:120px; display:inline-block;" autocomplete="off" class="layui-input" value="" readonly lay-verify="required" lay-reqText="请选择"><div style="display: inline-block; margin-left:5px; width: 80px;"><select lay-filter="end_time_b" id="end_time_b"></select></div>\
</td>\
</tr>\
</table>\
</form>';
layer.open({
type:1,
title:'调整工时',
area:['600px','388px'],
content:content,
success:function(){
//日期时间范围
laydate.render({
elem: '#start_time_a',
type: 'date',
max:0,
format: 'yyyy-MM-dd',
showBottom: false,
done:function(a,b,c){
$('#end_time_a').val(a);
}
});
//日期时间范围
laydate.render({
elem: '#end_time_a',
type: 'date',
max:0,
format: 'yyyy-MM-dd',
showBottom: false,
done:function(a,b,c){
$('#start_time_a').val(a);
}
});
$('#start_time_b,#end_time_b').empty();
var hourArray=[];
for(var h=0;h<24;h++){
var t=h<10?'0'+h:h
var t_1=t+':00',t_2=t+':15',t_3=t+':30',t_4=t+':45';
hourArray.push(t_1,t_2,t_3,t_4);
}
var html_1='', html_2='',def_h1='09:00',def_h2='09:30';
for(var s=0;s<hourArray.length;s++){
var check_1='',check_2='';
if(hourArray[s]==def_h1){
check_1='selected';
}
if(hourArray[s]==def_h2){
check_2='selected';
}
html_1 += '<option value="'+hourArray[s]+'" '+check_1+'>'+hourArray[s]+'</option>';
html_2 += '<option value="'+hourArray[s]+'" '+check_2+'>'+hourArray[s]+'</option>';
}
$('#start_time_b').append(html_1);
$('#end_time_b').append(html_2);
form.render();
},
btn: ['确定提交'],
btnAlign: 'c',
yes: function(idx){
let start_time_a = $('#start_time_a').val();
let end_time_a = $('#end_time_a').val();
let start_time_b = $('#start_time_b').val();
let end_time_b = $('#end_time_b').val();
if(start_time_a=='' || end_time_a==''){
layer.msg('请选择工作时间范围');
return;
}
$.ajax({
url: "/oa/api/update_schedule",
type:'post',
data:{
id:data.id,
admin_id:data.admin_id,
start_time_a:start_time_a,
end_time_a:end_time_a,
start_time_b:start_time_b,
end_time_b:end_time_b
},
success:function(e){
layer.msg(e.msg);
if(e.code==0){
layer.close(idx);
layui.scheduleTable.reload();
}
}
})
}
})
}
else if(obj.event === 'edit'){
work.add(0, obj.data);
}
else if(obj.event === 'view'){
work.view(obj.data);
}
else if(obj.event === 'del'){
layer.confirm('您确定要删除该工作记录?', {
icon: 3,
title: '提示'
}, function (index) {
let callback = function (e) {
layer.msg(e.msg);
if (e.code == 0) {
layui.scheduleTable.reload();
}
}
tool.delete("/oa/schedule/del", { id: obj.data.id }, callback);
layer.close(index);
});
}
});
}
</script>
{/block}
<!-- /脚本 -->
+255
View File
@@ -0,0 +1,255 @@
{extend name="../../base/view/common/base" /}
{block name="style"}
<style>
.layui-tab-title .layui-this{background-color:#fff;}
.layui-tab-card,.layui-card{box-shadow:0 0 0 0 rgb(0 0 0 / 10%); border-radius:0; border-top:none;}
.layui-card-tips {color: #969696;}
.layui-card-value {padding: 4px 0 16px;font-size: 18px;color: #1E9FFF;}
.content-content{line-height:1.6;}
.content-content p{margin-bottom:6px;}
.content-content img{max-width:100%; padding:8px; box-sizing: border-box;}
.log-item i{font-weight:800; color:#323232}
.log-content strong{margin:0 4px; color:#323232}
.layui-unselect dl {max-height:188px;}
.layui-table td.table-content img{max-width:100%; height:auto;}
</style>
{/block}
{block name="body"}
<div class="p-page">
<h3 class="pb-3">任务详情</h3>
<table class="layui-table layui-table-form" id="task">
<tr>
<td class="layui-td-gray">任务主题</td>
<td colspan="3" class="click-edit" data-types="text" data-field="title">{$detail.title|default=''}</td>
<td class="layui-td-gray">工作类型</td>
<td class="click-edit" data-types="picker" data-field="work_id" data-type="1" data-picker="workcate">{$detail.cate_name|default='-'}</td>
</tr>
<tr>
<td class="layui-td-gray">负责人员</td>
<td class="click-edit" data-types="adminpicker" data-field="director_uid" data-type="1" data-ids="{$detail.director_uid}" data-names="{$detail.director_name|default=''}">{$detail.director_name|default=''}</td>
<td class="layui-td-gray">协作人</td>
<td colspan="3" class="click-edit" data-types="adminpicker" data-field="assist_admin_ids" data-type="2" data-ids="{$detail.assist_admin_ids}" data-names="{$detail.assist_admin_names|default=''}">{$detail.assist_admin_names|default=''}</td>
</tr>
<tr>
<td class="layui-td-gray">优先级</td>
<td class="click-edit" data-types="dropdown" data-field="priority" data-array="priority" data-text="{$detail.priority}">{$detail.priority_name}</td>
<td class="layui-td-gray">关联项目</td>
<td colspan="3" class="click-edit" data-types="picker" data-field="project_id" data-type="1" data-picker="project">{$detail.project_name|default='-'}</td>
</tr>
<tr>
<td class="layui-td-gray">完成进度</td>
<td class="click-edit" data-types="num" data-field="done_ratio" data-text="{$detail.done_ratio}" data-min="0" data-max="100">{$detail.done_ratio|default='0'}%</td>
<td class="layui-td-gray">估计结束日期</td>
<td class="click-edit" data-types="oadate" data-field="end_time">{$detail.end_time|date='Y-m-d'}</span>{gt name="$detail.delay" value="0"}<span class="red">『逾期 {$detail.delay}天』</span>{/gt}</td>
<td class="layui-td-gray-3">估计工作量(工时)</td>
<td class="click-edit" data-types="num" data-field="plan_hours">{$detail.plan_hours|default=''}</td>
</tr>
<tr>
<td class="layui-td-gray">任务状态</td>
<td class="click-edit" data-types="dropdown" data-field="status" data-array="status" data-text="{$detail.status}">{$detail.status_name}</td>
<td class="layui-td-gray-2">实际完成日期</td>
<td>{eq name="$detail.over_time" value="0"}-{else/}{$detail.over_time|date='Y-m-d'}{/eq}</td>
<td class="layui-td-gray">实际工时</td>
<td>{eq name="$detail.work_hours" value="0"}-{else/}{$detail.work_hours}{/eq}</td>
</tr>
<tr>
<td class="layui-td-gray">相关附件 <button type="button" class="layui-btn layui-btn-xs" id="uploadBtn">附件上传</button></td>
<td colspan="5">
<div class="layui-row" id="uploadBox">
<input type="hidden" data-type="file" name="file_ids" value="{$detail.file_ids|default=''}">
{notempty name="$file_array"}
{volist name="$file_array" id="vo"}
<div class="layui-col-md4" id="uploadFile{$vo.id}">{:file_card($vo)}</div>
{/volist}
{/notempty}
</div>
</td>
</tr>
<tr>
<td colspan="6" class="click-edit" data-types="editor" data-field="content" data-target="content" style="background-color:#FAFAFA;"><strong>任务描述</strong></td>
</tr>
<tr>
<td colspan="6" id="content" class="table-content">{$detail.content|raw}</td>
</tr>
</table>
<div class="layui-tab layui-tab-card" style="margin:0;" lay-filter="docDemoTabBrief">
<ul class="layui-tab-title">
<li class="layui-this">工作记录({$detail.schedules})</li>
<li>员工评论(<span id="commentTotal">0</span>)</li>
<li>操作日志(<span id="logListTotal">0</span>)</li>
</ul>
<div class="layui-tab-content py-1 px-3" style="background-color:#fff;">
<div class="layui-tab-item layui-show py-2">
<table class="layui-hide" id="table_work" lay-filter="table_work"></table>
</div>
<div class="layui-tab-item comment-list">
<div class="comment-input my-2">
<input type="text" id="commentInput" readonly placeholder="发表一下你的看法" class="layui-input" value="">
</div>
<div id="commentBox"></div>
</div>
<div class="layui-tab-item logs-list">
<div id="logList" class="log-timeline p-3"></div>
</div>
</div>
</div>
</div>
<script type="text/html" id="toolbarDemo">
<div>
<div class="layui-input-inline" style="margin-right:12px;"><strong>工作日志</strong></div><div class="layui-input-inline"><button class="layui-btn layui-btn-xs" type="button" lay-event="add">+ </button></div>
</div>
</script>
{/block}
<!-- /主体 -->
<!-- 脚本 -->
{block name="script"}
<script>
const admin_id = '{$detail.admin_id}';
const detail_id = '{$detail.id}';
const status_array = {:json_encode(get_task_status(1),JSON_UNESCAPED_UNICODE)};
const priority_array = {:json_encode(get_priority(1),JSON_UNESCAPED_UNICODE)};
const moduleInit = ['tool','oaPicker','oaEdit','oaSchedule','oaComment','uploadPlus'];
function gouguInit() {
const oaEdit = layui.oaEdit,work = layui.oaSchedule,comment = layui.oaComment, uploadPlus = layui.uploadPlus, tool = layui.tool, table = layui.table;
oaEdit.log('logList','Task',detail_id);
comment.init({
"box":'commentBox',//容器id
"input": 'commentInput',
"topic_id":detail_id,
"module": 'task',
});
let edit_callback = function(e){
layer.msg(e.msg);
if(e.code==0){
setTimeout(function(){
location.reload();
},1000)
}
}
//附件上传
var attachment = new uploadPlus({
"target":'uploadBtn',
"targetBox":'uploadBox',
"attachment":{
"type":1,//0ajax多文件模式,1ajax单文件单记录模式
"uidDelete":true,//是否开启只有上传人自己才能删除自己的附件
"ajaxSave":function(res){
tool.post('/project/api/add_file',{'topic_id':detail_id,'file_id':res.data.id,'file_name': res.data.name, 'module': 'task'},edit_callback);
},
"ajaxDelete":function(file_id){
tool.delete('/project/api/delete_file', {id: file_id}, edit_callback);
}
}
});
//快捷编辑
oaEdit.init({
box:'task',
id:detail_id,
url: "/project/task/add",
dropdown:{
status:status_array,
priority:priority_array
}
});
//工作记录相关
layui.scheduleTable = table.render({
elem: "#table_work"
, toolbar: "#toolbarDemo"
,url: "/oa/api/get_schedule"
,page: true
,limit: 20
,where:{task_id:detail_id}
,cellMinWidth: 80
,cols: [[
{field:'id',width:80, title: 'ID号', align:'center'}
,{field:'title',title: '工作内容',minWidth:240}
,{field: 'name', title: '所属人员', align:'center',width:80}
,{field: 'start_time', title: '工作时间范围', align:'center',width:260,templet:function(d){
var html=d.start_time+' 至 '+d.end_time;
return html;
}}
,{field: 'labor_type_string', title: '工作类型', align:'center',width:80}
,{field: 'labor_time', title: '工时', align:'center',width:80}
,{field:'create_time', title: '创建时间',width:150,align:'center'}
,{width:90,fixed:'right',title: '操作', align:'center',ignoreExport:true,templet: function(d){
var html='';
var btn1='<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="view">详细</a>';
if(d.admin_id !== login_admin){
return btn1;
}
else{
var btn2='<a class="layui-btn layui-btn-xs" lay-event="edit">编辑</a>';
html = '<div class="layui-btn-group">'+btn1+btn2+'</div>';
return html;
}
}}
]]
});
table.on('toolbar(table_work)',function (obj) {
if (obj.event === 'add') {
work.add(detail_id,{'id':0});
}
});
table.on('tool(table_work)',function (obj) {
var checkStatus = table.checkStatus(obj.config.id); //获取选中行状态
var data = obj.data;
if (obj.event === 'view') {
work.view(data);
return;
}
if (obj.event === 'edit') {
work.add(data.task_id,data);
return;
}
if (obj.event === 'del') {
layer.confirm('确定要删除该内容吗?', { icon: 3, title: '提示' }, function (index) {
let callback = function (e) {
layer.msg(e.msg);
if (e.code == 0) {
obj.del();
}
}
tool.delete("/project/task/del", { id: data.id }, callback);
layer.close(index);
});
return;
}
});
//工作记录相关
$('.add-schedule').on('click', function () {
work.add(detail_id,{'id':0});
})
//回复
$('#comment_task_' + detail_id).on('click', '[data-event="replay"]', function () {
let pid = $(this).data('id');
let to_uid = $(this).data('uid');
let to_unames = $(this).data('unames');
comment.textarea(0,detail_id,'task','',pid,to_uid,to_unames);
})
//编辑
$('#comment_task_' + detail_id).on('click', '[data-event="edit"]', function () {
let id = $(this).data('id');
let content = $('#comment_' + id).data('content');
comment.textarea(id, detail_id, 'task', content,0,0,'');
})
//删除
$('#comment_task_' + detail_id).on('click', '[data-event="del"]', function () {
let id = $(this).data('id');
comment.del(id, detail_id, 'task');
})
}
</script>
{/block}
+124
View File
@@ -0,0 +1,124 @@
{extend name="../../base/view/common/base" /}
<!-- 主体 -->
{block name="body"}
<div class="p-page">
<table class="layui-hide" id="table_work_cate" lay-filter="table_work_cate"></table>
</div>
<script type="text/html" id="toolbarDemo">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-sm add-new" type="button">+ 添加工作类型</button>
</div>
</script>
{/block}
<!-- /主体 -->
<!-- 脚本 -->
{block name="script"}
<script>
const moduleInit = ['tool'];
function gouguInit() {
var table = layui.table, tool = layui.tool;
layui.pageTable = table.render({
elem: "#table_work_cate"
,toolbar: '#toolbarDemo'
,url: "/project/work/datalist"
,page: false
,limit: 999
,cellMinWidth: 80
,cols: [[
{field:'id',width:80, title: 'ID号', align:'center'}
,{field:'title',title: '类型名称'}
,{field:'status', title: '状态',width:80,align:'center',templet: function(d){
var html1='<span class="green">正常</span>';
var html2='<span class="yellow">禁用</span>';
if(d.status==1){
return html1;
}
else{
return html2;
}
}}
,{width:100,title: '操作', align:'center',ignoreExport:true,templet: function(d){
var html='';
var btn='<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="edit">编辑</a>';
var btn1='<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="disable">禁用</a>';
var btn2='<a class="layui-btn layui-btn-xs" lay-event="open">启用</a>';
if(d.status==1){
html = '<div class="layui-btn-group">'+btn+btn1+'</div>';
}
else{
html = '<div class="layui-btn-group">'+btn+btn2+'</div>';
}
return html;
}}
]]
});
table.on('tool(table_work_cate)',function (obj) {
if(obj.event === 'edit'){
add_cate(obj.data.id,obj.data.title);
}
if(obj.event === 'disable'){
layer.confirm('确定要禁用该记录吗?', {icon: 3, title:'提示'}, function(index){
let callback = function (e) {
layer.msg(e.msg);
if (e.code == 0) {
layui.pageTable.reload();
}
}
tool.post("/project/work/set", {id:obj.data.id,status: 0}, callback);
layer.close(index);
});
}
if(obj.event === 'open'){
layer.confirm('确定要启用该记录吗?', {icon: 3, title:'提示'}, function(index){
let callback = function (e) {
layer.msg(e.msg);
if (e.code == 0) {
layui.pageTable.reload();
}
}
tool.post("/project/work/set", {id:obj.data.id,status:1}, callback);
layer.close(index);
});
}
});
$('body').on('click','.add-new',function(){
add_cate(0,'');
});
function add_cate(id,val){
var title = '新增类型';
if(id>0){
title = '编辑类型';
}
layer.prompt({
title: title,
value: val,
yes: function(index, layero) {
// 获取文本框输入的值
var value = layero.find(".layui-layer-input").val();
if (value) {
let callback = function (e) {
layer.msg(e.msg);
if (e.code == 0) {
layui.pageTable.reload();
}
}
tool.post("/project/work/add", {id: id,title: value}, callback);
layer.close(index);
} else {
layer.msg('请填写类型名称');
}
}
})
}
}
</script>
{/block}
<!-- /脚本 -->
+16
View File
@@ -0,0 +1,16 @@
{extend name="../../base/view/common/base" /}
<!-- 主体 -->
{block name="body"}
<div class="p-page">
<h3 class="pb-3">工作类型详情</h3>
<table class="layui-table layui-table-form">
<tr>
<td class="layui-td-gray">工作类型名称</td>
<td>{$detail.title|default=''}"></td>
<td class="layui-td-gray">排序</td>
<td>{$detail.sort|default=0}</td>
</tr>
</table>
</div>
{/block}
<!-- /主体 -->