Merge branch 'main' into zentao_menu
This commit is contained in:
+2
-2
@@ -12,13 +12,13 @@ spec:
|
||||
container:
|
||||
image: hub.zentao.net/ci/git:2.45.2
|
||||
script:
|
||||
- git diff --name-only --diff-filter=d ${DRONE_COMMIT_AFTER} ${DRONE_COMMIT_BEFORE} | tee .changes
|
||||
- git diff --name-only --diff-filter=d ${GITFOX_COMMIT_AFTER} ${GITFOX_COMMIT_BEFORE} | tee .changes
|
||||
- |
|
||||
cat > sonar-project.properties <<EOL
|
||||
sonar.projectKey=$(echo $CI_REPO_NAME | tr '/' ':')
|
||||
sonar.sources=.
|
||||
sonar.language=php
|
||||
sonar.branch.name=${DRONE_TARGET_BRANCH}
|
||||
sonar.branch.name=${GITFOX_TARGET_BRANCH}
|
||||
sonar.inclusions=$(xargs < .changes | tr ' ' ',')
|
||||
sonar.working.directory=/tmp
|
||||
sonar.exclusions=**/extension/**/ext/**,**/*.bak,**/*.sql,**/*.js,**/*.css,**/*.yaml,**/*.zip,**/*.out,**/lang/*,doc/*
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
*/
|
||||
class bugConfirmEntry extends entry
|
||||
{
|
||||
/**
|
||||
/**
|
||||
* POST method.
|
||||
*
|
||||
* @param int $bugID
|
||||
@@ -19,9 +19,10 @@ class bugConfirmEntry extends entry
|
||||
* @return string
|
||||
**/
|
||||
public function post($bugID)
|
||||
{
|
||||
$fields = 'assignedTo,mailto,comment,pri,type';
|
||||
$this->batchSetPost($fields);
|
||||
{
|
||||
$fields = 'assignedTo,mailto,comment,pri,type,status,deadline';
|
||||
$bug = $this->loadModel('bug')->getByID($bugID);
|
||||
$this->batchSetPost($fields, $bug);
|
||||
|
||||
$control = $this->loadController('bug', 'confirm');
|
||||
$control->confirm($bugID);
|
||||
@@ -33,6 +34,6 @@ class bugConfirmEntry extends entry
|
||||
$bug = $this->loadModel('bug')->getById($bugID);
|
||||
|
||||
return $this->send(200, $this->format($bug, 'openedBy:user,openedDate:time,assignedTo:user,assignedDate:time,reviewedBy:user,reviewedDate:time,lastEditedBy:user,lastEditedDate:time,closedBy:user,closedDate:time,deleted:bool,mailto:userList'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ class bugResolveEntry extends entry
|
||||
|
||||
$data = $this->getData();
|
||||
if(!$data) return $this->send400('error');
|
||||
if(isset($data->status) and $data->status == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
|
||||
if(isset($data->result) and $data->result == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
|
||||
|
||||
$bug = $this->loadModel('bug')->getByID($bugID);
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ class executionCasesEntry extends entry
|
||||
if(empty($executionID)) return $this->sendError(400, 'Need execution id.');
|
||||
|
||||
$control = $this->loadController('execution', 'testcase');
|
||||
$control->testcase($executionID, $this->param('product', 0), $this->param('branch', 0), $this->param('status', 'all'), $this->param('module', 0), $this->param('order', 'id_desc'), 0, $this->param('limit', 20), $this->param('page', 1));
|
||||
$control->testcase($executionID, (int)$this->param('product', 0), $this->param('branch', 'all'), $this->param('status', 'all'), 0, (int)$this->param('module', 0), $this->param('order', 'id_desc'), 0, $this->param('limit', 20), $this->param('page', 1));
|
||||
|
||||
$data = $this->getData();
|
||||
|
||||
|
||||
@@ -127,18 +127,18 @@ class executionsEntry extends entry
|
||||
if(isset($data->result) and $data->result == 'fail') return $this->sendError(400, $data->message);
|
||||
|
||||
$account = $this->app->user->account;
|
||||
$projects = $data->data->projects;
|
||||
$dropMenu = array('involved' => array(), 'other' => array(), 'closed' => array());
|
||||
foreach($data->data->executions as $projectID => $projectExecutions)
|
||||
foreach($data->data->projects as $projectID => $projectName)
|
||||
{
|
||||
foreach($projectExecutions as $execution)
|
||||
if(!isset($data->data->projectExecutions->$projectID)) continue;
|
||||
|
||||
foreach($data->data->projectExecutions->$projectID as $execution)
|
||||
{
|
||||
if(helper::diffDate(date('Y-m-d'), $execution->end) > 0) $execution->delay = true;
|
||||
$teams = $execution->teams;
|
||||
$execution = $this->filterFields($execution, 'id,project,model,type,name,code,status,PM,delay');
|
||||
|
||||
$projectName = zget($projects, $execution->project, '');
|
||||
if($projectName) $execution->name = $projectName . '/' . $execution->name;
|
||||
$execution->name = $projectName . '/' . $execution->name;
|
||||
|
||||
if($execution->status == 'closed')
|
||||
{
|
||||
|
||||
@@ -36,7 +36,7 @@ class feedbackEntry extends entry
|
||||
if(!$data or !isset($data->status)) return $this->send400('error');
|
||||
if(isset($data->status) and $data->status == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
|
||||
|
||||
$feedback->actions = $this->loadModel('action')->processActionForAPI($data->data->actions, $data->data->users, $this->lang->feedback);
|
||||
$feedback->actions = $this->loadModel('action')->processActionForAPI($data->data->history, $data->data->users, $this->lang->feedback);
|
||||
return $this->send(200, $this->format($feedback, 'activatedDate:time,openedBy:user,openedDate:time,assignedTo:user,assignedDate:time,mailto:userList,resolvedBy:user,resolvedDate:time,closedBy:user,closedDate:time,lastEditedBy:user,lastEditedDate:time,deadline:date,deleted:bool'));
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ class feedbackEntry extends entry
|
||||
{
|
||||
$oldFeedback = $this->loadModel('feedback')->getById($feedbackID);
|
||||
|
||||
$fields = 'module,product,type,title,public,desc,status,feedbackBy,notifyEmail,notify,uid';
|
||||
$fields = 'module,product,type,title,public,desc,status,feedbackBy,notifyEmail,notify,uid,pri';
|
||||
$this->batchSetPost($fields, $oldFeedback);
|
||||
|
||||
$control = $this->loadController('feedback', 'edit');
|
||||
|
||||
@@ -22,6 +22,7 @@ class feedbacksEntry extends entry
|
||||
if(strpos(strtolower($this->param('fields')), 'moduleandproduct') !== false) return $this->getModuleAndProduct();
|
||||
|
||||
$control = $this->loadController('feedback', 'admin');
|
||||
$control->session->set('feedbackProduct', 0);
|
||||
$control->admin($this->param('status', 'unclosed'), 0, $this->param('orderBy', 'id_desc'), 0, $this->param('limit', 20), $this->param('page', 1));
|
||||
$data = $this->getData();
|
||||
|
||||
@@ -54,7 +55,7 @@ class feedbacksEntry extends entry
|
||||
*/
|
||||
public function post()
|
||||
{
|
||||
$fields = 'module,product,type,title,public,desc,status,feedbackBy,notify,uid';
|
||||
$fields = 'module,product,type,title,public,desc,status,feedbackBy,notify,uid,pri';
|
||||
$this->batchSetPost($fields);
|
||||
|
||||
$this->setPost('notifyEmail', $this->request('notifyEmail', ''));
|
||||
|
||||
@@ -23,7 +23,7 @@ class modulesEntry extends entry
|
||||
$objectID = $this->param('id', '');
|
||||
|
||||
if(!$objectType or !$objectID) return $this->sendError(400, 'Need id and type.');
|
||||
if(!in_array($objectType, array('story', 'task', 'bug', 'case'))) return $this->sendError(400, 'Type is not allowed');
|
||||
if(!in_array($objectType, array('story', 'task', 'bug', 'case', 'feedback', 'product'))) return $this->sendError(400, 'Type is not allowed');
|
||||
|
||||
if($objectType == 'task')
|
||||
{
|
||||
|
||||
@@ -31,6 +31,7 @@ class projectEntry extends entry
|
||||
if(isset($data->status) and $data->status == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
|
||||
|
||||
$project = $this->format($data->data->project, 'openedBy:user,openedDate:time,lastEditedBy:user,lastEditedDate:time,closedBy:user,closedDate:time,canceledBy:user,canceledDate:time,realBegan:date,realEnd:date,PM:user,whitelist:userList,deleted:bool');
|
||||
if(!$project->multiple) $project->executionID = $this->loadModel('execution')->getNoMultipleID($projectID);
|
||||
|
||||
$this->loadModel('testcase');
|
||||
$project->caseReview = ($this->config->testcase->needReview or !empty($this->config->testcase->forceReview));
|
||||
|
||||
@@ -27,7 +27,7 @@ class projectCasesEntry extends entry
|
||||
$this->app->session->set('project', $projectID, $this->app->tab);
|
||||
|
||||
$control = $this->loadController('project', 'testcase');
|
||||
$control->testcase($projectID, $this->param('product', 0), $this->param('branch', 0), $this->param('status', 'all'), 0, $this->param('order', 'id_desc'), 0, $this->param('limit', 20), $this->param('page', 1));
|
||||
$control->testcase($projectID, $this->param('product', 0), $this->param('branch', 'all'), $this->param('status', 'all'), 0, $this->param('caseType', ''), $this->param('order', 'id_desc'), 0, $this->param('limit', 20), $this->param('page', 1));
|
||||
|
||||
$data = $this->getData();
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ class releasesEntry extends entry
|
||||
if(empty($productID)) return $this->sendError(400, 'Need product id.');
|
||||
|
||||
$control = $this->loadController('release', 'browse');
|
||||
$control->browse($productID, $this->param('branch', 0), $this->param('status', 'all'), $this->param('order', 't1.date_desc'), 0, $this->param('limit', 20), $this->param('page', 1));
|
||||
$control->browse($productID, $this->param('branch', 'all'), $this->param('status', 'all'), $this->param('order', 't1.date_desc'), '', 0, $this->param('limit', 20), $this->param('page', 1));
|
||||
|
||||
/* Response */
|
||||
$data = $this->getData();
|
||||
|
||||
@@ -162,9 +162,9 @@ class reportsEntry extends entry
|
||||
$newProject->id = $project->id;
|
||||
$newProject->name = $project->name;
|
||||
$newProject->status = $project->status;
|
||||
$newProject->progress = round($project->hours->progress, 1);
|
||||
$newProject->totalConsumed = round($project->hours->totalConsumed, 1);
|
||||
$newProject->totalLeft = round($project->hours->totalLeft, 1);
|
||||
$newProject->progress = round($project->progress, 1);
|
||||
$newProject->totalConsumed = round($project->totalConsumed, 1);
|
||||
$newProject->totalLeft = round($project->totalLeft, 1);
|
||||
if(isset($project->delay)) $newProject->delay = $project->delay;
|
||||
|
||||
$statusList['all']['total'] += 1;
|
||||
@@ -206,9 +206,9 @@ class reportsEntry extends entry
|
||||
$newExecution->id = $execution->id;
|
||||
$newExecution->name = $execution->name;
|
||||
$newExecution->status = $execution->status;
|
||||
$newExecution->progress = round($execution->hours->progress, 1);
|
||||
$newExecution->totalConsumed = round($execution->hours->totalConsumed, 1);
|
||||
$newExecution->totalLeft = round($execution->hours->totalLeft, 1);
|
||||
$newExecution->progress = round($execution->progress, 1);
|
||||
$newExecution->totalConsumed = round($execution->totalConsumed, 1);
|
||||
$newExecution->totalLeft = round($execution->totalLeft, 1);
|
||||
if(isset($execution->delay)) $newExecution->delay = $execution->delay;
|
||||
|
||||
$statusList['all']['total'] += 1;
|
||||
|
||||
@@ -33,6 +33,7 @@ class storiesEntry extends entry
|
||||
$stories = $data->data->stories;
|
||||
$pager = $data->data->pager;
|
||||
$requirements = $this->loadModel('story')->getRequirements($productID);
|
||||
if(!empty($requirements)) $requirements = $this->story->getByList(array_keys($requirements));
|
||||
|
||||
$result = array();
|
||||
$this->loadModel('product');
|
||||
|
||||
@@ -96,7 +96,7 @@ class storyEntry extends entry
|
||||
$oldStory = $this->loadModel('story')->getByID($storyID);
|
||||
|
||||
/* Set $_POST variables. */
|
||||
$fields = 'title,product,parent,reviewer,type,plan,module,source,sourceNote,category,pri,estimate,mailto,keywords,uid,stage,notifyEmail';
|
||||
$fields = 'title,product,parent,reviewer,type,plan,module,source,sourceNote,category,pri,estimate,mailto,keywords,uid,stage,notifyEmail,status';
|
||||
$this->batchSetPost($fields, $oldStory);
|
||||
|
||||
$control = $this->loadController('story', 'edit');
|
||||
|
||||
@@ -29,8 +29,8 @@ class storyActiveEntry extends entry
|
||||
$control->activate($storyID);
|
||||
|
||||
$data = $this->getData();
|
||||
if(!$data or !isset($data->status)) return $this->send400('error');
|
||||
if(isset($data->status) and $data->status == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
|
||||
if(!$data || !isset($data->result)) return $this->send400('error');
|
||||
if($data->result == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
|
||||
|
||||
$story = $this->loadModel('story')->getByID($storyID);
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ class storyAssignToEntry extends entry
|
||||
|
||||
$data = $this->getData();
|
||||
if(!$data) return $this->send400('error');
|
||||
if(isset($data->status) and $data->status == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
|
||||
if(isset($data->result) and $data->result == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
|
||||
|
||||
$story = $this->loadModel('story')->getByID($storyID);
|
||||
|
||||
|
||||
@@ -26,10 +26,12 @@ class storyChangeEntry extends entry
|
||||
$this->batchSetPost($fields);
|
||||
$fields = 'title,spec,verify';
|
||||
$this->batchSetPost($fields, $oldStory);
|
||||
$this->setPost('status', 'reviewing');
|
||||
|
||||
/* If reviewer is not post, set needNotReview. */
|
||||
if(!$this->request('reviewer'))
|
||||
{
|
||||
$this->setPost('status', $oldStory->status);
|
||||
$this->setPost('reviewer', array());
|
||||
$this->setPost('needNotReview', 1);
|
||||
}
|
||||
@@ -37,11 +39,12 @@ class storyChangeEntry extends entry
|
||||
$control = $this->loadController('story', 'change');
|
||||
$this->requireFields('title');
|
||||
|
||||
$control->change($storyID);
|
||||
$control->change($storyID, '', $oldStory->type);
|
||||
|
||||
$data = $this->getData();
|
||||
if(!$data or !isset($data->status)) return $this->send400('error');
|
||||
if(isset($data->status) and $data->status == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
|
||||
if(!$data) return $this->send400('error');
|
||||
if(isset($data->result) && $data->result == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
|
||||
if(isset($data->status) && $data->status == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
|
||||
|
||||
$story = $this->loadModel('story')->getByID($storyID);
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ class storyCloseEntry extends entry
|
||||
|
||||
$data = $this->getData();
|
||||
if(!$data) return $this->send400('error');
|
||||
if(isset($data->status) and $data->status == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
|
||||
if(isset($data->result) && $data->result == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
|
||||
|
||||
$story = $this->loadModel('story')->getByID($storyID);
|
||||
|
||||
|
||||
@@ -21,10 +21,9 @@ class storyRecallEntry extends entry
|
||||
public function delete($storyID)
|
||||
{
|
||||
$control = $this->loadController('story', 'recall');
|
||||
$control->recall($storyID);
|
||||
$control->recall($storyID, 'view', 'yes', $this->param('type', 'story'));
|
||||
|
||||
$this->getData();
|
||||
return $this->sendSuccess(200, 'success');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,10 +33,11 @@ class taskEntry extends entry
|
||||
$task = $data->data->task;
|
||||
|
||||
if(!empty($task->children)) $task->children = array_values((array)$task->children);
|
||||
if($task->parent > 0) $task->parentPri = $this->dao->select('pri')->from(TABLE_TASK)->where('id')->eq($task->parent)->fetch('pri');
|
||||
if($task->parent > 0) $task->parentPri = $this->dao->findById($task->parent)->from(TABLE_TASK)->fetch('pri');
|
||||
|
||||
/* Set execution name */
|
||||
$task->executionName = $data->data->execution->name;
|
||||
$task->executionName = $data->data->execution->name;
|
||||
$task->executionStatus = $data->data->execution->status;
|
||||
|
||||
/* Set module title */
|
||||
$moduleTitle = '';
|
||||
@@ -87,8 +88,6 @@ class taskEntry extends entry
|
||||
$task->preAndNext['pre'] = $preAndNext->pre ? $preAndNext->pre->id : '';
|
||||
$task->preAndNext['next'] = $preAndNext->next ? $preAndNext->next->id : '';
|
||||
|
||||
$execution = $this->loadModel('project')->getByID($task->execution, 'execution,sprint');
|
||||
$task->executionStatus = $execution->status;
|
||||
|
||||
return $this->send(200, $this->format($task, 'deadline:date,openedBy:user,openedDate:time,assignedTo:user,assignedDate:time,realStarted:time,finishedBy:user,finishedDate:time,closedBy:user,closedDate:time,canceledBy:user,canceledDate:time,lastEditedBy:user,lastEditedDate:time,deleted:bool,mailto:userList'));
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ class taskBatchCreateEntry extends entry
|
||||
if(!$data) return $this->send400('error');
|
||||
if(isset($data->data->result) and $data->data->result == 'fail') return $this->sendError(400, $data->data->message);
|
||||
|
||||
$tasks = $this->loadModel('task')->getByIdList($data->idList);
|
||||
$tasks = $this->loadModel('task')->getByIdList((array)$data->idList);
|
||||
return $this->send(200, array('task' => $tasks));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,27 +50,16 @@ class taskRecordEstimateEntry extends entry
|
||||
*/
|
||||
public function post($taskID)
|
||||
{
|
||||
if($this->loadModel('effort'))
|
||||
{
|
||||
$fields = 'id,dates,consumed,left,objectType,objectID,work';
|
||||
$this->batchSetPost($fields);
|
||||
$control = $this->loadController('effort', 'createForObject');
|
||||
$control->createForObject('task', $taskID);
|
||||
}
|
||||
else
|
||||
{
|
||||
$fields = 'id,dates,consumed,left,work';
|
||||
$this->batchSetPost($fields);
|
||||
$control = $this->loadController('task', 'recordWorkhour');
|
||||
$control->recordWorkhour($taskID);
|
||||
}
|
||||
$fields = 'date,consumed,left,work';
|
||||
$this->batchSetPost($fields);
|
||||
$control = $this->loadController('task', 'recordWorkhour');
|
||||
$control->recordWorkhour($taskID);
|
||||
|
||||
$data = $this->getData();
|
||||
if(!$data) return $this->send400('error');
|
||||
if(isset($data->status) and $data->status == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
|
||||
if(isset($data->result) and $data->result == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
|
||||
|
||||
$task = $this->loadModel('task')->getById($taskID);
|
||||
|
||||
return $this->send(200, $this->format($task, 'deadline:date,openedBy:user,openedDate:time,assignedTo:user,assignedDate:time,realStarted:time,finishedBy:user,finishedDate:time,closedBy:user,closedDate:time,canceledBy:user,canceledDate:time,lastEditedBy:user,lastEditedDate:time,deleted:bool,mailto:userList'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,19 @@ class testcaseEntry extends entry
|
||||
|
||||
$case = $data->case;
|
||||
$case->steps = (isset($case->steps) and !empty($case->steps)) ? array_values(get_object_vars((object)$case->steps)) : array();
|
||||
if(!empty($case->steps))
|
||||
{
|
||||
foreach($case->steps as &$step)
|
||||
{
|
||||
foreach(array('step', 'desc', 'expect') as $field)
|
||||
{
|
||||
if(isset($step->$field) && $step->$field != '')
|
||||
{
|
||||
$step->$field = str_replace(array("&quot;", "&#039;", "&lt;", "&gt;"), array('"', "'", "<", ">"), $step->$field);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->send(200, $this->format($case, 'openedBy:user,openedDate:time,lastEditedBy:user,lastEditedDate:time,lastRunDate:time,scriptedDate:date,reviewedBy:user,reviewedDate:date,steps:array,deleted:bool'));
|
||||
}
|
||||
|
||||
@@ -166,9 +166,9 @@ class userEntry extends entry
|
||||
{
|
||||
$myExecutions['owner'] = array();
|
||||
$myExecutions['other'] = array();
|
||||
foreach($data->data->executions as $projectID => $projectExecutions)
|
||||
foreach($data->data->projectExecutions as $executionList)
|
||||
{
|
||||
foreach($projectExecutions as $execution)
|
||||
foreach($executionList as $execution)
|
||||
{
|
||||
if($execution->status == 'done' or $execution->status == 'closed') continue;
|
||||
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ if(!class_exists('config')){class config{}}
|
||||
if(!function_exists('getWebRoot')){function getWebRoot(){}}
|
||||
|
||||
/* 基本设置。Basic settings. */
|
||||
$config->version = '20.5'; // ZenTaoPHP的版本。 The version of ZenTaoPHP. Don't change it.
|
||||
$config->version = '20.6'; // ZenTaoPHP的版本。 The version of ZenTaoPHP. Don't change it.
|
||||
$config->liteVersion = '1.2'; // 迅捷版版本。 The version of Lite.
|
||||
$config->charset = 'UTF-8'; // ZenTaoPHP的编码。 The encoding of ZenTaoPHP.
|
||||
$config->cookieLife = time() + 2592000; // Cookie的生存时间。The cookie life time.
|
||||
|
||||
@@ -756,7 +756,7 @@ $config->db->sqliteBlacklist = array('sqlite_queue', 'cron');
|
||||
$config->hasDropmenuApps = array('program', 'project', 'product', 'execution', 'qa', 'admin', 'bi', 'feedback', 'demandpool');
|
||||
$config->hasBranchMenuModules = array('product', 'story', 'release', 'bug', 'testcase', 'testtask', 'branch', 'tree');
|
||||
$config->excludeBranchMenu = array('product-dashboard', 'product-view');
|
||||
$config->excludeDropmenuList = array('program-browse', 'program-productview', 'program-kanban', 'product-all', 'product-index', 'product-kanban', 'project-kanban', 'execution-all', 'execution-executionkanban', 'project-browse', 'project-batchedit', 'product-batchedit', 'admin-index', 'product-create', 'project-create', 'execution-create', 'program-create', 'execution-batchedit', 'metric-preview', 'metric-browse', 'metric-view', 'metriclib-browse', 'qa-index', 'caselib-create', 'feedback-batchedit', 'feedback-batchclose', 'feedback-showimport', 'dimension-browse', 'dataview-browse', 'dataview-query');
|
||||
$config->excludeDropmenuList = array('program-browse', 'program-productview', 'program-kanban', 'product-all', 'product-index', 'product-kanban', 'project-kanban', 'execution-all', 'execution-executionkanban', 'project-browse', 'project-batchedit', 'product-batchedit', 'admin-index', 'product-create', 'project-create', 'execution-create', 'program-create', 'execution-batchedit', 'metric-preview', 'metric-browse', 'metric-view', 'metriclib-browse', 'qa-index', 'caselib-create', 'feedback-batchedit', 'feedback-batchclose', 'feedback-showimport', 'dimension-browse', 'dataview-browse', 'dataview-query', 'ticket-batchedit', 'ticket-batchfinish', 'ticket-batchactivate');
|
||||
$config->hasSwitcherModules = array('design');
|
||||
$config->hasSwitcherMethods = array('project-bug', 'project-testcase', 'execution-bug', 'execution-testcase', 'testtask-cases', 'testtask-view', 'testtask-report', 'testtask-groupcase', 'testtask-linkcase');
|
||||
$config->excludeSwitcherList = array();
|
||||
|
||||
+5
-5
@@ -418,11 +418,11 @@ REPLACE INTO `zt_product` (`id`, `program`, `name`, `code`, `shadow`, `bind`, `l
|
||||
(2, 1, '企业内部工时管理系统', '', 0, '0', 0, 'normal', 'normal', '', '', 'productManager', '', 'productManager', '', '', 'open', '', '', '', NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 'admin', '2024-04-02 14:30:55', '20.0.beta2', NULL, 10, 'rnd', '0');
|
||||
REPLACE INTO `zt_productplan` (`id`, `product`, `branch`, `parent`, `title`, `status`, `desc`, `begin`, `end`, `finishedDate`, `closedDate`, `order`, `closedReason`, `createdBy`, `createdDate`, `deleted`) VALUES
|
||||
(1, 1, '0', 0, '1.0版本', 'wait', '<p><span style=\"font-size:13px;font-family:\'Source Han Sans CN\', PingFangSC, \'Microsoft YaHei\', HiraginoSansGB, Roboto, Helvetica, Tahoma, sans-serif;\">开发出企业网站1.0版本。</span></p>', '2030-01-01', '2030-01-01', NULL, NULL, '0', '', 'admin', '2024-04-02 14:42:47', '0');
|
||||
REPLACE INTO `zt_project` (`id`, `project`, `charter`, `model`, `type`, `category`, `lifetime`, `budget`, `budgetUnit`, `attribute`, `percent`, `milestone`, `output`, `auth`, `parent`, `path`, `grade`, `name`, `code`, `hasProduct`, `begin`, `end`, `firstEnd`, `realBegan`, `realEnd`, `days`, `status`, `subStatus`, `pri`, `desc`, `version`, `parentVersion`, `planDuration`, `realDuration`, `progress`, `estimate`, `left`, `consumed`, `teamCount`, `market`, `openedBy`, `openedDate`, `openedVersion`, `lastEditedBy`, `lastEditedDate`, `closedBy`, `closedDate`, `closedReason`, `canceledBy`, `canceledDate`, `suspendedDate`, `PO`, `PM`, `QD`, `RD`, `team`, `acl`, `whitelist`, `order`, `vision`, `stageBy`, `displayCards`, `fluidBoard`, `multiple`, `parallel`, `colWidth`, `minColWidth`, `maxColWidth`, `deleted`) VALUES
|
||||
(1, 0, 0, '', 'program', '', '', '', 'CNY', '', 0, '0', NULL, '', 0, ',1,', 1, '企业管理', '', 1, '2024-04-02', '2059-12-31', NULL, '2024-04-02', NULL, 0, 'doing', '', '1', '', 0, 0, 0, 0, 0.00, 0, 0, 0, 0, 0, 'admin', '2024-04-02 14:25:12', '', '', NULL, '', NULL, '', '', NULL, NULL, '', '0', '', '', '', 'open', 'admin,projectManager', 5, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'),
|
||||
(2, 0, 0, 'scrum', 'project', '', '', '', 'CNY', '', 0, '0', NULL, 'extend', 1, ',1,2,', 2, '企业管理系统', '', 1, '2024-04-02', '2059-12-31', '2059-12-31', '2024-04-02', NULL, 0, 'doing', '', '1', '', 0, 0, 0, 0, 24.50, 52, 40, 13, 3, 0, 'admin', '2024-04-02 14:34:13', '', 'admin', '2024-04-02 14:34:13', '', NULL, '', '', NULL, NULL, '', 'projectManager', '', '', '企业管理系统', 'open', '', 10, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'),
|
||||
(3, 2, 0, '', 'sprint', '', 'long', '0', 'CNY', '', 0, '0', NULL, '', 2, ',2,3,', 1, '企业网站第一期', '', 1, '2024-04-02', '2024-04-15', '2024-04-15', '2024-04-02', NULL, 10, 'doing', '', '1', '', 0, 0, 0, 0, 24.50, 52, 40, 13, 0, 0, 'admin', '2024-04-02 14:35:31', '20.0.beta2', 'admin', '2024-04-02 14:35:31', '', NULL, '', '', NULL, NULL, 'productManager', 'projectManager', '', 'productManager', '企业网站第一期 ', 'open', '', 15, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'),
|
||||
(4, 2, 0, '', 'sprint', '', 'long', '0', 'CNY', '', 0, '0', NULL, '', 2, ',2,4,', 1, '企业网站第二期', '', 1, '2024-04-02', '2024-05-02', NULL, NULL, NULL, 23, 'wait', '', '1', '', 0, 0, 0, 0, 0.00, 0, 0, 0, 0, 0, 'admin', '2024-04-02 14:36:20', '20.0.beta2', 'admin', '2024-04-02 14:36:20', '', NULL, '', '', NULL, NULL, 'productManager', 'projectManager', 'admin', 'productManager', '企业网站第二期', 'open', '', 20, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0');
|
||||
REPLACE INTO `zt_project` (`id`, `project`, `charter`, `model`, `type`, `category`, `lifetime`, `budget`, `budgetUnit`, `attribute`, `percent`, `milestone`, `output`, `auth`, `storyType`, `parent`, `path`, `grade`, `name`, `code`, `hasProduct`, `begin`, `end`, `firstEnd`, `realBegan`, `realEnd`, `days`, `status`, `subStatus`, `pri`, `desc`, `version`, `parentVersion`, `planDuration`, `realDuration`, `progress`, `estimate`, `left`, `consumed`, `teamCount`, `market`, `openedBy`, `openedDate`, `openedVersion`, `lastEditedBy`, `lastEditedDate`, `closedBy`, `closedDate`, `closedReason`, `canceledBy`, `canceledDate`, `suspendedDate`, `PO`, `PM`, `QD`, `RD`, `team`, `acl`, `whitelist`, `order`, `vision`, `stageBy`, `displayCards`, `fluidBoard`, `multiple`, `parallel`, `colWidth`, `minColWidth`, `maxColWidth`, `deleted`) VALUES
|
||||
(1, 0, 0, '', 'program', '', '', '', 'CNY', '', 0, '0', NULL, '', '', 0, ',1,', 1, '企业管理', '', 1, '2024-04-02', '2059-12-31', NULL, '2024-04-02', NULL, 0, 'doing', '', '1', '', 0, 0, 0, 0, 0.00, 0, 0, 0, 0, 0, 'admin', '2024-04-02 14:25:12', '', '', NULL, '', NULL, '', '', NULL, NULL, '', '0', '', '', '', 'open', 'admin,projectManager', 5, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'),
|
||||
(2, 0, 0, 'scrum', 'project', '', '', '', 'CNY', '', 0, '0', NULL, 'extend', 'story,requirement', 1, ',1,2,', 2, '企业管理系统', '', 1, '2024-04-02', '2059-12-31', '2059-12-31', '2024-04-02', NULL, 0, 'doing', '', '1', '', 0, 0, 0, 0, 24.50, 52, 40, 13, 3, 0, 'admin', '2024-04-02 14:34:13', '', 'admin', '2024-04-02 14:34:13', '', NULL, '', '', NULL, NULL, '', 'projectManager', '', '', '企业管理系统', 'open', '', 10, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'),
|
||||
(3, 2, 0, '', 'sprint', '', 'long', '0', 'CNY', '', 0, '0', NULL, '', '', 2, ',2,3,', 1, '企业网站第一期', '', 1, '2024-04-02', '2024-04-15', '2024-04-15', '2024-04-02', NULL, 10, 'doing', '', '1', '', 0, 0, 0, 0, 24.50, 52, 40, 13, 0, 0, 'admin', '2024-04-02 14:35:31', '20.0.beta2', 'admin', '2024-04-02 14:35:31', '', NULL, '', '', NULL, NULL, 'productManager', 'projectManager', '', 'productManager', '企业网站第一期 ', 'open', '', 15, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'),
|
||||
(4, 2, 0, '', 'sprint', '', 'long', '0', 'CNY', '', 0, '0', NULL, '', '', 2, ',2,4,', 1, '企业网站第二期', '', 1, '2024-04-02', '2024-05-02', NULL, NULL, NULL, 23, 'wait', '', '1', '', 0, 0, 0, 0, 0.00, 0, 0, 0, 0, 0, 'admin', '2024-04-02 14:36:20', '20.0.beta2', 'admin', '2024-04-02 14:36:20', '', NULL, '', '', NULL, NULL, 'productManager', 'projectManager', 'admin', 'productManager', '企业网站第二期', 'open', '', 20, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0');
|
||||
REPLACE INTO `zt_projectadmin` (`group`, `account`, `programs`, `projects`, `products`, `executions`) VALUES
|
||||
(1, 'admin', NULL, '2', NULL, NULL);
|
||||
REPLACE INTO `zt_projectcase` (`project`, `product`, `case`, `count`, `version`, `order`) VALUES
|
||||
|
||||
+5
-5
@@ -418,11 +418,11 @@ REPLACE INTO `zt_product` (`id`, `program`, `name`, `code`, `shadow`, `bind`, `l
|
||||
(2, 1, '企业内部工时管理系统', '', 0, '0', 0, 'normal', 'normal', '', '', 'productManager', '', 'productManager', '', '', 'open', '', '', '', NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 'admin', '2024-04-02 14:30:55', '20.0.beta2', NULL, 10, 'rnd', '0');
|
||||
REPLACE INTO `zt_productplan` (`id`, `product`, `branch`, `parent`, `title`, `status`, `desc`, `begin`, `end`, `finishedDate`, `closedDate`, `order`, `closedReason`, `createdBy`, `createdDate`, `deleted`) VALUES
|
||||
(1, 1, '0', 0, '1.0版本', 'wait', '<p><span style=\"font-size:13px;font-family:\'Source Han Sans CN\', PingFangSC, \'Microsoft YaHei\', HiraginoSansGB, Roboto, Helvetica, Tahoma, sans-serif;\">开发出企业网站1.0版本。</span></p>', '2030-01-01', '2030-01-01', NULL, NULL, '0', '', 'admin', '2024-04-02 14:42:47', '0');
|
||||
REPLACE INTO `zt_project` (`id`, `project`, `charter`, `model`, `type`, `category`, `lifetime`, `budget`, `budgetUnit`, `attribute`, `percent`, `milestone`, `output`, `auth`, `parent`, `path`, `grade`, `name`, `code`, `hasProduct`, `begin`, `end`, `firstEnd`, `realBegan`, `realEnd`, `days`, `status`, `subStatus`, `pri`, `desc`, `version`, `parentVersion`, `planDuration`, `realDuration`, `progress`, `estimate`, `left`, `consumed`, `teamCount`, `market`, `openedBy`, `openedDate`, `openedVersion`, `lastEditedBy`, `lastEditedDate`, `closedBy`, `closedDate`, `closedReason`, `canceledBy`, `canceledDate`, `suspendedDate`, `PO`, `PM`, `QD`, `RD`, `team`, `acl`, `whitelist`, `order`, `vision`, `stageBy`, `displayCards`, `fluidBoard`, `multiple`, `parallel`, `colWidth`, `minColWidth`, `maxColWidth`, `deleted`) VALUES
|
||||
(1, 0, 0, '', 'program', '', '', '', 'CNY', '', 0, '0', NULL, '', 0, ',1,', 1, '企业管理', '', 1, '2024-04-02', '2059-12-31', NULL, '2024-04-02', NULL, 0, 'doing', '', '1', '', 0, 0, 0, 0, 0.00, 0, 0, 0, 0, 0, 'admin', '2024-04-02 14:25:12', '', '', NULL, '', NULL, '', '', NULL, NULL, '', '0', '', '', '', 'open', 'admin,projectManager', 5, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'),
|
||||
(2, 0, 0, 'scrum', 'project', '', '', '', 'CNY', '', 0, '0', NULL, 'extend', 1, ',1,2,', 2, '企业管理系统', '', 1, '2024-04-02', '2059-12-31', '2059-12-31', '2024-04-02', NULL, 0, 'doing', '', '1', '', 0, 0, 0, 0, 24.50, 52, 40, 13, 3, 0, 'admin', '2024-04-02 14:34:13', '', 'admin', '2024-04-02 14:34:13', '', NULL, '', '', NULL, NULL, '', 'projectManager', '', '', '企业管理系统', 'open', '', 10, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'),
|
||||
(3, 2, 0, '', 'sprint', '', 'long', '0', 'CNY', '', 0, '0', NULL, '', 2, ',2,3,', 1, '企业网站第一期', '', 1, '2024-04-02', '2024-04-15', '2024-04-15', '2024-04-02', NULL, 10, 'doing', '', '1', '', 0, 0, 0, 0, 24.50, 52, 40, 13, 0, 0, 'admin', '2024-04-02 14:35:31', '20.0.beta2', 'admin', '2024-04-02 14:35:31', '', NULL, '', '', NULL, NULL, 'productManager', 'projectManager', '', 'productManager', '企业网站第一期 ', 'open', '', 15, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'),
|
||||
(4, 2, 0, '', 'sprint', '', 'long', '0', 'CNY', '', 0, '0', NULL, '', 2, ',2,4,', 1, '企业网站第二期', '', 1, '2024-04-02', '2024-05-02', NULL, NULL, NULL, 23, 'wait', '', '1', '', 0, 0, 0, 0, 0.00, 0, 0, 0, 0, 0, 'admin', '2024-04-02 14:36:20', '20.0.beta2', 'admin', '2024-04-02 14:36:20', '', NULL, '', '', NULL, NULL, 'productManager', 'projectManager', 'admin', 'productManager', '企业网站第二期', 'open', '', 20, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0');
|
||||
REPLACE INTO `zt_project` (`id`, `project`, `charter`, `model`, `type`, `category`, `lifetime`, `budget`, `budgetUnit`, `attribute`, `percent`, `milestone`, `output`, `auth`, `storyType`, parent`, `path`, `grade`, `name`, `code`, `hasProduct`, `begin`, `end`, `firstEnd`, `realBegan`, `realEnd`, `days`, `status`, `subStatus`, `pri`, `desc`, `version`, `parentVersion`, `planDuration`, `realDuration`, `progress`, `estimate`, `left`, `consumed`, `teamCount`, `market`, `openedBy`, `openedDate`, `openedVersion`, `lastEditedBy`, `lastEditedDate`, `closedBy`, `closedDate`, `closedReason`, `canceledBy`, `canceledDate`, `suspendedDate`, `PO`, `PM`, `QD`, `RD`, `team`, `acl`, `whitelist`, `order`, `vision`, `stageBy`, `displayCards`, `fluidBoard`, `multiple`, `parallel`, `colWidth`, `minColWidth`, `maxColWidth`, `deleted`) VALUES
|
||||
(1, 0, 0, '', 'program', '', '', '', 'CNY', '', 0, '0', NULL, '', '', 0, ',1,', 1, '企业管理', '', 1, '2024-04-02', '2059-12-31', NULL, '2024-04-02', NULL, 0, 'doing', '', '1', '', 0, 0, 0, 0, 0.00, 0, 0, 0, 0, 0, 'admin', '2024-04-02 14:25:12', '', '', NULL, '', NULL, '', '', NULL, NULL, '', '0', '', '', '', 'open', 'admin,projectManager', 5, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'),
|
||||
(2, 0, 0, 'scrum', 'project', '', '', '', 'CNY', '', 0, '0', NULL, 'extend', 'story,requirement', 1, ',1,2,', 2, '企业管理系统', '', 1, '2024-04-02', '2059-12-31', '2059-12-31', '2024-04-02', NULL, 0, 'doing', '', '1', '', 0, 0, 0, 0, 24.50, 52, 40, 13, 3, 0, 'admin', '2024-04-02 14:34:13', '', 'admin', '2024-04-02 14:34:13', '', NULL, '', '', NULL, NULL, '', 'projectManager', '', '', '企业管理系统', 'open', '', 10, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'),
|
||||
(3, 2, 0, '', 'sprint', '', 'long', '0', 'CNY', '', 0, '0', NULL, '', '', 2, ',2,3,', 1, '企业网站第一期', '', 1, '2024-04-02', '2024-04-15', '2024-04-15', '2024-04-02', NULL, 10, 'doing', '', '1', '', 0, 0, 0, 0, 24.50, 52, 40, 13, 0, 0, 'admin', '2024-04-02 14:35:31', '20.0.beta2', 'admin', '2024-04-02 14:35:31', '', NULL, '', '', NULL, NULL, 'productManager', 'projectManager', '', 'productManager', '企业网站第一期 ', 'open', '', 15, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'),
|
||||
(4, 2, 0, '', 'sprint', '', 'long', '0', 'CNY', '', 0, '0', NULL, '', '', 2, ',2,4,', 1, '企业网站第二期', '', 1, '2024-04-02', '2024-05-02', NULL, NULL, NULL, 23, 'wait', '', '1', '', 0, 0, 0, 0, 0.00, 0, 0, 0, 0, 0, 'admin', '2024-04-02 14:36:20', '20.0.beta2', 'admin', '2024-04-02 14:36:20', '', NULL, '', '', NULL, NULL, 'productManager', 'projectManager', 'admin', 'productManager', '企业网站第二期', 'open', '', 20, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0');
|
||||
REPLACE INTO `zt_projectadmin` (`group`, `account`, `programs`, `projects`, `products`, `executions`) VALUES
|
||||
(1, 'admin', NULL, '2', NULL, NULL);
|
||||
REPLACE INTO `zt_projectcase` (`project`, `product`, `case`, `count`, `version`, `order`) VALUES
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -41,12 +41,3 @@ UPDATE `zt_kanbancard` SET `color` = '#ff9f46' WHERE `color` = '#2a5f29';
|
||||
INSERT INTO `zt_cron`(`m`, `h`, `dom`, `mon`, `dow`, `command`, `remark`, `type`, `buildin`, `status`, `lastTime`) VALUES ('0', '*/1', '*', '*', '*', 'moduleName=metric&methodName=updateDashboardMetricLib', '计算仪表盘数据', 'zentao', 1, 'normal', NUll);
|
||||
|
||||
UPDATE `zt_todo` SET `type` = 'custom' WHERE `type` = 'cycle' AND `cycle` = 0;
|
||||
|
||||
INSERT INTO `zt_metric` (`purpose`, `scope`, `object`, `stage`, `type`, `name`, `code`, `alias`, `unit`, `desc`, `definition`, `when`, `createdBy`, `createdDate`, `builtin`, `deleted`, `dateType`) VALUES
|
||||
('scale', 'product', 'feedback', 'released', 'php', '按产品统计的每周新增反馈数', 'count_of_weekly_created_feedback_in_product', '新增反馈数', 'count', '按产品统计的每周新增反馈数是指在一个周内收集到的用户反馈的数量。这个度量项可以帮助团队了解用户对产品的发展趋势和需求变化,并进行产品策略的调整和优化', '产品中创建时间为某个周的反馈的个数求和\r\n过滤已删除的反馈\r\n过滤已删除的产品', 'realtime', 'system', '2024-05-07 08:00:00', '1', '0', 'week'),
|
||||
('scale', 'product', 'feedback', 'released', 'php', '按产品统计的处理中的反馈数', 'count_of_doing_feedback_in_product', '处理中反馈数', 'count', '按产品统计的处理中的反馈数表示产品中状态为处理中的反馈数量之和。该数值越大,说明团队并行处理的反馈越多,可以帮助团队了解当前的工作负载情况', '产品中所有反馈个数求和\r\n状态为处理中\r\n过滤已删除的反馈\r\n过滤已删除的产品', 'realtime', 'system', '2024-05-07 08:00:00', '1', '0', 'nodate'),
|
||||
('scale', 'product', 'feedback', 'released', 'php', '按产品统计的已处理的反馈数', 'count_of_done_feedback_in_product', '已处理反馈数', 'count', '按产品统计的已处理的反馈数表示产品中状态为已处理的反馈数量之和。该数值越大,说明团队成员处理的反馈越多,有利于提高用户满意度', '产品中所有反馈个数求和\r\n状态为已处理\r\n过滤已删除的反馈\r\n过滤已删除的产品', 'realtime', 'system', '2024-05-07 08:00:00', '1', '0', 'nodate'),
|
||||
('scale', 'product', 'feedback', 'released', 'php', '按产品统计的待完善的反馈数', 'count_of_clarify_feedback_in_product', '待完善反馈数', 'count', '按产品统计的待完善的反馈数表示产品中状态为待完善的反馈数量之和。该数值越大,说明有较多的反馈信息不清晰或比较复杂。需要反馈者更多的澄清和解释', '产品中所有反馈个数求和\r\n状态为待完善\r\n过滤已删除的反馈\r\n过滤已删除的产品', 'realtime', 'system', '2024-05-07 08:00:00', '1', '0', 'nodate'),
|
||||
('scale', 'product', 'feedback', 'released', 'php', '按产品统计的待处理的反馈数', 'count_of_wait_feedback_in_product', '待处理反馈数', 'count', '按产品统计的待处理的反馈数表示产品中状态为待处理的反馈数量之和。该度量项可能暗示产品团队的反馈处理效率,待处理反馈数越多,可能会导致客户满意度降低', '产品中所有反馈个数求和\r\n状态为待处理\r\n过滤已删除的反馈\r\n过滤已删除的产品', 'realtime', 'system', '2024-05-07 08:00:00', '1', '0', 'nodate'),
|
||||
('scale', 'product', 'feedback', 'released', 'php', '按产品统计的追问中的反馈数', 'count_of_asked_feedback_in_product', '追问中反馈数', 'count', '按产品统计的追问中的反馈数表示产品中状态为追问中的反馈数量之和。该度量项可能暗示着反馈的复杂性或对处理方案的疑惑,追问中的反馈数量越多,可能意味着团队需要更多时间和资源来回复并解决这些问题', '产品中所有反馈个数求和\r\n状态为追问中\r\n过滤已删除的反馈\r\n过滤已删除的产品', 'realtime', 'system', '2024-05-07 08:00:00', '1', '0', 'nodate'),
|
||||
('scale', 'product', 'feedback', 'released', 'php', '按产品统计的未关闭的反馈数', 'count_of_unclosed_feedback_in_product', '未关闭反馈数', 'count', '按产品统计的未关闭的反馈数表示产品中状态为未关闭的反馈数量之和。这个度量项可以一定程度反映产品团队响应用户反馈的效率和及时处理用户问题的能力', '产品中所有反馈个数求和\r\n过滤状态为已关闭的反馈\r\n过滤已删除的反馈\r\n过滤已删除的产品', 'realtime', 'system', '2024-05-07 08:00:00', '1', '0', 'nodate');
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
CREATE TABLE IF NOT EXISTS `zt_workflowui` (
|
||||
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`module` varchar(30) NOT NULL,
|
||||
`action` varchar(50) NOT NULL,
|
||||
`name` varchar(30) NOT NULL,
|
||||
`conditions` text NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
CREATE INDEX `module` ON `zt_workflowui` (`module`);
|
||||
CREATE INDEX `action` ON `zt_workflowui` (`action`);
|
||||
|
||||
ALTER TABLE `zt_workflowlayout` ADD `ui` mediumint(8) unsigned NOT NULL DEFAULT '0' AFTER `action`;
|
||||
ALTER TABLE `zt_workflowrelationlayout` ADD `ui` mediumint(8) unsigned NOT NULL DEFAULT '0' AFTER `action`;
|
||||
|
||||
ALTER TABLE `zt_approvalflow` CHANGE `type` `workflow` char(30) NOT NULL DEFAULT '';
|
||||
UPDATE `zt_approvalflow` SET `workflow` = '';
|
||||
|
||||
DROP INDEX `unique` ON `zt_workflowlayout`;
|
||||
CREATE UNIQUE INDEX `unique` ON `zt_workflowlayout`(`module`,`action`,`ui`,`field`,`vision`);
|
||||
|
||||
DROP INDEX `unique` ON `zt_workflowrelationlayout`;
|
||||
CREATE UNIQUE INDEX `unique` ON `zt_workflowrelationlayout`(`prev`, `next`, `action`,`ui`,`field`);
|
||||
|
||||
UPDATE `zt_workflowaction` SET `method`='create', `type`='single' WHERE `module`='epic' AND `action`='create';
|
||||
UPDATE `zt_workflowaction` SET `method`='batchcreate', `type`='batch' WHERE `module`='epic' AND `action`='batchcreate';
|
||||
UPDATE `zt_workflowaction` SET `method`='batchoperate', `type`='batch' WHERE `module`='epic' AND `action`='batchedit';
|
||||
UPDATE `zt_workflowaction` SET `method`='exporttemplate', `type`='single' WHERE `module`='epic' AND `action`='exporttemplate';
|
||||
UPDATE `zt_workflowaction` SET `method`='import', `type`='single' WHERE `module`='epic' AND `action`='import';
|
||||
UPDATE `zt_workflowaction` SET `method`='showimport', `type`='single' WHERE `module`='epic' AND `action`='showimport';
|
||||
UPDATE `zt_workflowaction` SET `method`='edit', `type`='single' WHERE `module`='epic' AND `action`='edit';
|
||||
UPDATE `zt_workflowaction` SET `method`='view', `type`='single' WHERE `module`='epic' AND `action`='view';
|
||||
UPDATE `zt_workflowaction` SET `method`='delete', `type`='single' WHERE `module`='epic' AND `action`='delete';
|
||||
UPDATE `zt_workflowaction` SET `method`='operate', `type`='single' WHERE `module`='epic' AND `action`='close';
|
||||
UPDATE `zt_workflowaction` SET `method`='operate', `type`='single' WHERE `module`='epic' AND `action`='activate';
|
||||
UPDATE `zt_workflowaction` SET `method`='operate', `type`='single' WHERE `module`='epic' AND `action`='assignTo';
|
||||
UPDATE `zt_workflowaction` SET `method`='operate', `type`='single' WHERE `module`='epic' AND `action`='review';
|
||||
UPDATE `zt_workflowaction` SET `method`='operate', `type`='single' WHERE `module`='epic' AND `action`='change';
|
||||
+19
-255
@@ -179,7 +179,7 @@ CREATE TABLE IF NOT EXISTS `zt_approvalflow` (
|
||||
`version` mediumint(8) NOT NULL DEFAULT '1',
|
||||
`createdBy` varchar(30) NOT NULL DEFAULT '',
|
||||
`createdDate` datetime NULL,
|
||||
`type` varchar(30) NOT NULL DEFAULT '',
|
||||
`workflow` varchar(30) NOT NULL DEFAULT '',
|
||||
`deleted` tinyint(4) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
@@ -2440,256 +2440,6 @@ INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('
|
||||
INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'story', '', 'gradeRule', 'stepwise');
|
||||
INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'requirement', '', 'gradeRule', 'stepwise');
|
||||
INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'epic', '', 'gradeRule', 'stepwise');
|
||||
-- DROP TABLE IF EXISTS `zt_im_chat`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_im_chat` (
|
||||
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`gid` char(40) NOT NULL DEFAULT '',
|
||||
`name` varchar(60) NOT NULL DEFAULT '',
|
||||
`type` varchar(20) NOT NULL DEFAULT 'group',
|
||||
`admins` varchar(255) NOT NULL DEFAULT '',
|
||||
`committers` varchar(255) NOT NULL DEFAULT '',
|
||||
`subject` mediumint(8) unsigned NOT NULL DEFAULT 0,
|
||||
`public` enum('0', '1') NOT NULL DEFAULT '0',
|
||||
`createdBy` varchar(30) NOT NULL DEFAULT '',
|
||||
`createdDate` datetime NULL,
|
||||
`ownedBy` varchar(30) NOT NULL DEFAULT '',
|
||||
`editedBy` varchar(30) NOT NULL DEFAULT '',
|
||||
`editedDate` datetime NULL,
|
||||
`mergedDate` datetime NULL,
|
||||
`lastActiveTime` datetime NULL,
|
||||
`lastMessage` int(11) unsigned NOT NULL DEFAULT 0,
|
||||
`lastMessageIndex` int(11) unsigned NOT NULL DEFAULT 0,
|
||||
`dismissDate` datetime NULL,
|
||||
`pinnedMessages` text NULL,
|
||||
`mergedChats` text NULL,
|
||||
`adminInvite` enum('0','1') NOT NULL DEFAULT '0',
|
||||
`avatar` text NULL,
|
||||
`archiveDate` datetime NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
CREATE INDEX `gid` ON `zt_im_chat` (`gid`);
|
||||
CREATE INDEX `name` ON `zt_im_chat` (`name`);
|
||||
CREATE INDEX `type` ON `zt_im_chat` (`type`);
|
||||
CREATE INDEX `public` ON `zt_im_chat` (`public`);
|
||||
CREATE INDEX `createdBy` ON `zt_im_chat` (`createdBy`);
|
||||
CREATE INDEX `editedBy` ON `zt_im_chat` (`editedBy`);
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_im_chatuser`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_im_chatuser` (
|
||||
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`cgid` char(40) NOT NULL DEFAULT '',
|
||||
`user` mediumint(8) NOT NULL DEFAULT 0,
|
||||
`order` smallint(5) NOT NULL DEFAULT 0,
|
||||
`star` enum('0', '1') NOT NULL DEFAULT '0',
|
||||
`hide` enum('0', '1') NOT NULL DEFAULT '0',
|
||||
`mute` enum('0', '1') NOT NULL DEFAULT '0',
|
||||
`freeze` enum('0', '1') NOT NULL DEFAULT '0',
|
||||
`join` datetime NULL,
|
||||
`quit` datetime NULL,
|
||||
`category` varchar(40) NOT NULL DEFAULT '',
|
||||
`lastReadMessage` int(11) unsigned NOT NULL DEFAULT 0,
|
||||
`lastReadMessageIndex` int(11) unsigned NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
CREATE UNIQUE INDEX `chatuser` ON `zt_im_chatuser`(`cgid`, `user`);
|
||||
CREATE INDEX `cgid` ON `zt_im_chatuser` (`cgid`);
|
||||
CREATE INDEX `user` ON `zt_im_chatuser` (`user`);
|
||||
CREATE INDEX `order` ON `zt_im_chatuser` (`order`);
|
||||
CREATE INDEX `star` ON `zt_im_chatuser` (`star`);
|
||||
CREATE INDEX `hide` ON `zt_im_chatuser` (`hide`);
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_im_client`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_im_client` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`version` char(30) NOT NULL DEFAULT '',
|
||||
`desc` varchar(100) NOT NULL DEFAULT '',
|
||||
`changeLog` text NULL,
|
||||
`strategy` varchar(10) NOT NULL DEFAULT '',
|
||||
`downloads` text NULL,
|
||||
`createdDate` datetime NULL,
|
||||
`createdBy` varchar(30) NOT NULL DEFAULT '',
|
||||
`editedDate` datetime NULL,
|
||||
`editedBy` varchar(30) NOT NULL DEFAULT '',
|
||||
`status` enum('released','wait') NOT NULL DEFAULT 'wait',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_im_message`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_im_message` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`gid` char(40) NOT NULL DEFAULT '',
|
||||
`cgid` char(40) NOT NULL DEFAULT '',
|
||||
`user` varchar(30) NOT NULL DEFAULT '',
|
||||
`date` datetime NULL,
|
||||
`index` int(11) unsigned NOT NULL DEFAULT 0,
|
||||
`type` enum('normal','broadcast','notify','bulletin','botcommand') NOT NULL DEFAULT 'normal',
|
||||
`content` text NULL,
|
||||
`contentType` enum('text', 'plain', 'emotion', 'image', 'file', 'object', 'code') NOT NULL DEFAULT 'text',
|
||||
`data` text NULL,
|
||||
`deleted` enum('0','1') NOT NULL DEFAULT '0',
|
||||
`legacy` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`uniqueIndex` int(11) GENERATED ALWAYS AS (CASE WHEN `legacy` = 1 THEN NULL WHEN `cgid` = 'notification' THEN NULL ELSE `index` END) STORED,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
CREATE INDEX `mgid` ON `zt_im_message` (`gid`);
|
||||
CREATE INDEX `mcgid` ON `zt_im_message` (`cgid`);
|
||||
CREATE INDEX `muser` ON `zt_im_message` (`user`);
|
||||
CREATE INDEX `mtype` ON `zt_im_message` (`type`);
|
||||
CREATE UNIQUE INDEX `uniqueIndexInChat` ON `zt_im_message`(`cgid`, `uniqueIndex`);
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_im_message_backup`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_im_message_backup` (
|
||||
`id` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
`gid` char(40) NOT NULL DEFAULT '',
|
||||
`cgid` char(40) NOT NULL DEFAULT '',
|
||||
`user` varchar(30) NOT NULL DEFAULT '',
|
||||
`date` datetime NULL,
|
||||
`index` int(11) unsigned NOT NULL DEFAULT 0,
|
||||
`type` enum('normal', 'broadcast', 'notify') NOT NULL DEFAULT 'normal',
|
||||
`content` text NULL,
|
||||
`contentType` enum('text', 'plain', 'emotion', 'image', 'file', 'object', 'code') NOT NULL DEFAULT 'text',
|
||||
`data` text NULL,
|
||||
`deleted` enum('0','1') NOT NULL DEFAULT '0'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_im_message_index`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_im_message_index` (
|
||||
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`tableName` char(64) NOT NULL DEFAULT '',
|
||||
`start` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
`end` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
`startDate` datetime NULL,
|
||||
`endDate` datetime NULL,
|
||||
`chats` text NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
CREATE INDEX `tableName` ON `zt_im_message_index` (`tableName`);
|
||||
CREATE INDEX `start` ON `zt_im_message_index` (`start`);
|
||||
CREATE INDEX `end` ON `zt_im_message_index` (`end`);
|
||||
CREATE INDEX `startDate` ON `zt_im_message_index` (`startDate`);
|
||||
CREATE INDEX `endDate` ON `zt_im_message_index` (`endDate`);
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_im_chat_message_index`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_im_chat_message_index` (
|
||||
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`gid` char(40) NOT NULL DEFAULT '',
|
||||
`tableName` char(64) NOT NULL DEFAULT '',
|
||||
`start` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
`end` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
`startIndex` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
`endIndex` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
`startDate` datetime NULL,
|
||||
`endDate` datetime NULL,
|
||||
`count` mediumint(8) unsigned NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
CREATE UNIQUE INDEX `chattable` ON `zt_im_chat_message_index`(`gid`,`tableName`);
|
||||
CREATE INDEX `start` ON `zt_im_chat_message_index` (`start`);
|
||||
CREATE INDEX `end` ON `zt_im_chat_message_index` (`end`);
|
||||
CREATE INDEX `startDate` ON `zt_im_chat_message_index` (`startDate`);
|
||||
CREATE INDEX `endDate` ON `zt_im_chat_message_index` (`endDate`);
|
||||
CREATE INDEX `chatstartindex` ON `zt_im_chat_message_index` (`gid`, `startIndex`);
|
||||
CREATE INDEX `chatendindex` ON `zt_im_chat_message_index` (`gid`, `endIndex`);
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_im_messagestatus`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_im_messagestatus` (
|
||||
`user` mediumint(8) NOT NULL DEFAULT 0,
|
||||
`message` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
`status` enum('waiting','sent','readed','deleted') NOT NULL DEFAULT 'waiting'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
CREATE UNIQUE INDEX `user` ON `zt_im_messagestatus`(`user`,`message`);
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_im_queue`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_im_queue` (
|
||||
`id` mediumint(8) unsigned NOT NULL auto_increment,
|
||||
`type` char(30) NOT NULL DEFAULT '',
|
||||
`content` text NULL,
|
||||
`addDate` datetime NULL,
|
||||
`processDate` datetime NULL,
|
||||
`result` text NULL,
|
||||
`status` char(30) NOT NULL DEFAULT '',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_im_conference`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_im_conference` (
|
||||
`id` mediumint(8) unsigned NOT NULL auto_increment,
|
||||
`rid` char(40) NOT NULL DEFAULT '',
|
||||
`cgid` char(40) NOT NULL DEFAULT '',
|
||||
`status` enum ('closed', 'open', 'notStarted', 'canceled') NOT NULL DEFAULT 'closed',
|
||||
`participants` text NULL,
|
||||
`subscribers` text NULL,
|
||||
`invitee` text NULL,
|
||||
`openedBy` mediumint(8) NOT NULL DEFAULT 0,
|
||||
`openedDate` datetime NULL,
|
||||
`topic` text NULL,
|
||||
`startTime` datetime NULL,
|
||||
`endTime` datetime NULL,
|
||||
`password` char(20) NOT NULL DEFAULT '',
|
||||
`type` enum('default', 'periodic', 'scheduled') NOT NULL DEFAULT 'default',
|
||||
`number` char(20) NOT NULL DEFAULT '',
|
||||
`note` text NULL,
|
||||
`sentNotify` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`reminderTime` int NOT NULL DEFAULT 0,
|
||||
`moderators` text NOT NULL,
|
||||
`isPrivate` enum ('0', '1') NOT NULL DEFAULT '0',
|
||||
`isInner` enum('0', '1') NOT NULL DEFAULT '1',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
CREATE INDEX `status` ON `zt_im_conference` (`status`);
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_im_conferenceaction`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_im_conferenceaction` (
|
||||
`id` mediumint(8) unsigned NOT NULL auto_increment,
|
||||
`rid` char(40) NOT NULL DEFAULT '',
|
||||
`type` enum('create','invite','join','leave','close','publish') NOT NULL DEFAULT 'create',
|
||||
`data` text NULL,
|
||||
`user` mediumint(8) NOT NULL DEFAULT 0,
|
||||
`date` datetime NULL,
|
||||
`device` char(40) NOT NULL DEFAULT 'default',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_im_conferenceuser`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_im_conferenceuser` (
|
||||
`id` mediumint(8) unsigned NOT NULL auto_increment,
|
||||
`conference` mediumint(8) NOT NULL DEFAULT 0,
|
||||
`user` mediumint(8) NOT NULL DEFAULT 0,
|
||||
`hide` enum('0', '1') NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
CREATE UNIQUE INDEX `conferenceuser` ON `zt_im_conferenceuser`(`conference`, `user`);
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_im_conferenceinvite`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_im_conferenceinvite` (
|
||||
`id` mediumint(8) unsigned NOT NULL auto_increment,
|
||||
`conferenceID` mediumint(8) unsigned NOT NULL,
|
||||
`inviteeID` mediumint(8) unsigned NOT NULL,
|
||||
`status` enum('pending', 'accepted', 'rejected') NOT NULL DEFAULT 'pending',
|
||||
`createdDate` datetime NULL,
|
||||
`updatedDate` datetime NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
CREATE UNIQUE INDEX `conference_user` ON `zt_im_conferenceinvite` (`conferenceID`, `inviteeID`);
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_im_userdevice`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_im_userdevice` (
|
||||
`id` mediumint(8) unsigned NOT NULL auto_increment,
|
||||
`user` mediumint(8) NOT NULL DEFAULT 0,
|
||||
`device` char(40) NOT NULL DEFAULT 'default',
|
||||
`deviceID` char(40) NOT NULL DEFAULT '',
|
||||
`token` char(64) NOT NULL DEFAULT '',
|
||||
`validUntil` datetime NULL,
|
||||
`lastLogin` datetime NULL,
|
||||
`lastLogout` datetime NULL,
|
||||
`online` tinyint(1) DEFAULT 0 NOT NULL,
|
||||
`version` char(10) DEFAULT '' NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
CREATE UNIQUE INDEX `userdevice` ON `zt_im_userdevice`(`user`, `device`);
|
||||
CREATE INDEX `user` ON `zt_im_userdevice` (`user`);
|
||||
CREATE INDEX `lastLogin` ON `zt_im_userdevice` (`lastLogin`);
|
||||
CREATE INDEX `lastLogout` ON `zt_im_userdevice` (`lastLogout`);
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_relationoftasks`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_relationoftasks` (
|
||||
@@ -13572,6 +13322,7 @@ CREATE TABLE IF NOT EXISTS `zt_workflowlayout` (
|
||||
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`module` varchar(30) NOT NULL DEFAULT '',
|
||||
`action` varchar(50) NOT NULL DEFAULT '',
|
||||
`ui` mediumint(8) NOT NULL DEFAULT 0,
|
||||
`field` varchar(50) NOT NULL DEFAULT '',
|
||||
`order` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`width` varchar(50) NOT NULL DEFAULT '0',
|
||||
@@ -13584,7 +13335,7 @@ CREATE TABLE IF NOT EXISTS `zt_workflowlayout` (
|
||||
`vision` varchar(10) NOT NULL DEFAULT 'rnd',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
CREATE UNIQUE INDEX `unique` ON `zt_workflowlayout`(`module`,`action`,`field`,`vision`);
|
||||
CREATE UNIQUE INDEX `unique` ON `zt_workflowlayout`(`module`,`action`,`ui`,`field`,`vision`);
|
||||
CREATE INDEX `module` ON `zt_workflowlayout` (`module`);
|
||||
CREATE INDEX `action` ON `zt_workflowlayout` (`action`);
|
||||
CREATE INDEX `order` ON `zt_workflowlayout` (`order`);
|
||||
@@ -13640,11 +13391,12 @@ CREATE TABLE IF NOT EXISTS `zt_workflowrelationlayout` (
|
||||
`prev` varchar(30) NOT NULL DEFAULT '',
|
||||
`next` varchar(30) NOT NULL DEFAULT '',
|
||||
`action` varchar(50) NOT NULL DEFAULT '',
|
||||
`ui` mediumint(8) NOT NULL DEFAULT 0,
|
||||
`field` varchar(50) NOT NULL DEFAULT '',
|
||||
`order` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
CREATE UNIQUE INDEX `unique` ON `zt_workflowrelationlayout`(`prev`, `next`, `action`, `field`);
|
||||
CREATE UNIQUE INDEX `unique` ON `zt_workflowrelationlayout`(`prev`, `next`, `action`,`ui`,`field`);
|
||||
CREATE INDEX `prev` ON `zt_workflowrelationlayout` (`prev`);
|
||||
CREATE INDEX `next` ON `zt_workflowrelationlayout` (`next`);
|
||||
CREATE INDEX `action` ON `zt_workflowrelationlayout` (`action`);
|
||||
@@ -13700,6 +13452,18 @@ CREATE UNIQUE INDEX `moduleversion` ON `zt_workflowversion`(`module`, `version`)
|
||||
CREATE INDEX `module` ON `zt_workflowversion` (`module`);
|
||||
CREATE INDEX `version` ON `zt_workflowversion` (`version`);
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_workflowui`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_workflowui` (
|
||||
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`module` varchar(30) NOT NULL,
|
||||
`action` varchar(50) NOT NULL,
|
||||
`name` varchar(30) NOT NULL,
|
||||
`conditions` text NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
CREATE INDEX `module` ON `zt_workflowui` (`module`);
|
||||
CREATE INDEX `action` ON `zt_workflowui` (`action`);
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_workflowreport`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_workflowreport` (
|
||||
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
|
||||
@@ -14689,8 +14453,8 @@ CREATE TABLE `zt_scene` (
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
REPLACE INTO `zt_approvalflow` (`id`, `name`, `code`, `desc`, `version`, `createdBy`, `createdDate`, `type`, `deleted`) VALUES
|
||||
(1, '最简审批', 'simple', '', 1, 'admin', '2022-04-29 08:46:40', 'project', 0);
|
||||
REPLACE INTO `zt_approvalflow` (`id`, `name`, `code`, `desc`, `version`, `createdBy`, `createdDate`, `workflow`, `deleted`) VALUES
|
||||
(1, '最简审批', 'simple', '', 1, 'admin', '2022-04-29 08:46:40', '', 0);
|
||||
|
||||
REPLACE INTO `zt_approvalflowspec` (`id`, `flow`, `version`, `nodes`, `createdBy`, `createdDate`) VALUES
|
||||
(1, 1, 1, '[{\"type\":\"start\",\"ccs\":[]},{\"id\":\"3ewcj92p55e\",\"type\":\"approval\",\"title\":\"审批\",\"reviewType\":\"manual\",\"multiple\":\"and\",\"agentType\":\"pass\",\"reviewers\":[{\"type\":\"select\"}],\"ccs\":[]},{\"type\":\"end\",\"ccs\":[]}]', 'admin', '2022-04-29 08:46:40');
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
2024-08-30 20.6
|
||||
完成的需求
|
||||
修复的Bug
|
||||
|
||||
2024-08-16 20.5
|
||||
完成的需求:
|
||||
开源版:
|
||||
@@ -155,6 +159,43 @@
|
||||
IPD版:
|
||||
53821 任务列表不显示导出模版、导入excel按钮
|
||||
|
||||
2024-08-09 18.13
|
||||
修复的Bug:
|
||||
开源版:
|
||||
52925 测试单无法导出用例信息
|
||||
53054 [达梦]18版本燃尽图修改首日工时不生效
|
||||
53410 任务标题输入空格能直接保存成功
|
||||
53580 已解决的Bug,解决版本调整后没有将Bug自动关联至调整后版本的解决的Bug中
|
||||
53594 下载聊天后端error
|
||||
53623 运营界面没有导出用户的相关权限
|
||||
企业版:
|
||||
53217 用户需求通过工作流对评审页面增加字段不显示
|
||||
53572 8.13升级报错
|
||||
53585 测试单的日志剩余工时不显示
|
||||
旗舰版:
|
||||
52928 BI中报表拖动分组顺序不生效
|
||||
52972 达梦数据库兼容问题导致工作流有些模块无法应用
|
||||
53023 [达梦] 项目仪表盘项目周报区块报错
|
||||
53024 [达梦] 项目仪表盘估算区块报错
|
||||
53025 [达梦] 产品—发布详情页面报错
|
||||
53082 [达梦] BI图表查询数据失败
|
||||
53104 [达梦]BI数据表保存报错502
|
||||
53105 【达梦】数据表,点击中间表报错502
|
||||
53106 【达梦】重算全部度量项页面报错
|
||||
53232 【达梦】第一次设置审批流点击保存报错
|
||||
53233 【达梦】关闭审批流报错
|
||||
53240 【达梦】看板切换泳道方式报错
|
||||
53312 工作流审批选不到审批人
|
||||
53447 需求导出页面报错
|
||||
53582 开源版1253升级到旗舰版4.13报错
|
||||
IPD版:
|
||||
53612 开始市场阶段出现报错
|
||||
53613 编辑调研阶段保存时提示“里程碑不符合格式”
|
||||
53614 设置了阶段可以并行时,执行下添加的IPD项目子阶段无法开始
|
||||
53620 OR界面导出用户内容为空
|
||||
53622 轻量模式-无执行项目需求批量转任务保存有报错
|
||||
53644 ipd1.5升级到ipd2.1出现了报错
|
||||
|
||||
2024-08-02 20.4
|
||||
完成的需求:
|
||||
开源版:
|
||||
|
||||
@@ -1038,13 +1038,21 @@ if($config->edition != 'open')
|
||||
/* Feedback. */
|
||||
$lang->resource->feedback = new stdclass();
|
||||
$lang->resource->feedback->create = 'create';
|
||||
$lang->resource->feedback->batchCreate = 'batchCreate';
|
||||
$lang->resource->feedback->edit = 'edit';
|
||||
$lang->resource->feedback->browse = 'browse';
|
||||
$lang->resource->feedback->view = 'view';
|
||||
$lang->resource->feedback->comment = 'comment';
|
||||
$lang->resource->feedback->ask = 'ask';
|
||||
$lang->resource->feedback->delete = 'delete';
|
||||
$lang->resource->feedback->close = 'closeAction';
|
||||
$lang->resource->feedback->activate = 'activate';
|
||||
$lang->resource->feedback->batchEdit = 'batchEdit';
|
||||
$lang->resource->feedback->batchClose = 'batchClose';
|
||||
$lang->resource->feedback->batchAssignTo = 'batchAssignTo';
|
||||
$lang->resource->feedback->export = 'exportAction';
|
||||
$lang->resource->feedback->import = 'import';
|
||||
$lang->resource->feedback->exportTemplate = 'exportTemplate';
|
||||
$lang->resource->feedback->assignTo = 'assignAction';
|
||||
$lang->resource->feedback->products = 'products';
|
||||
$lang->resource->feedback->manageProduct = 'manageProduct';
|
||||
@@ -1069,6 +1077,11 @@ if($config->edition != 'open')
|
||||
$lang->resource->ticket->exportTemplate = 'exportTemplate';
|
||||
$lang->resource->ticket->import = 'import';
|
||||
$lang->resource->ticket->export = 'exportAction';
|
||||
$lang->resource->ticket->batchCreate = 'batchCreate';
|
||||
$lang->resource->ticket->batchEdit = 'batchEdit';
|
||||
$lang->resource->ticket->batchActivate = 'batchActivate';
|
||||
$lang->resource->ticket->batchFinish = 'batchFinish';
|
||||
$lang->resource->ticket->batchAssignTo = 'batchAssignTo';
|
||||
|
||||
/* Attend */
|
||||
$lang->resource->attend = new stdclass();
|
||||
@@ -1349,11 +1362,18 @@ if($config->edition != 'open')
|
||||
|
||||
/* workflowlayout */
|
||||
$lang->resource->workflowlayout = new stdclass();
|
||||
$lang->resource->workflowlayout->admin = 'admin';
|
||||
$lang->resource->workflowlayout->block = 'block';
|
||||
$lang->resource->workflowlayout->admin = 'admin';
|
||||
$lang->resource->workflowlayout->block = 'block';
|
||||
$lang->resource->workflowlayout->addUI = 'addUI';
|
||||
$lang->resource->workflowlayout->editUI = 'editUI';
|
||||
$lang->resource->workflowlayout->deleteUI = 'deleteUI';
|
||||
|
||||
|
||||
$lang->workflowlayout->methodOrder[5] = 'admin';
|
||||
$lang->workflowlayout->methodOrder[10] = 'block';
|
||||
$lang->workflowlayout->methodOrder[15] = 'addUI';
|
||||
$lang->workflowlayout->methodOrder[20] = 'editUI';
|
||||
$lang->workflowlayout->methodOrder[25] = 'deleteUI';
|
||||
|
||||
/* workflowlinkage */
|
||||
$lang->resource->workflowlinkage = new stdclass();
|
||||
|
||||
@@ -381,12 +381,13 @@ class baseEntry
|
||||
{
|
||||
$output = helper::removeUTF8Bom(ob_get_clean());
|
||||
$output = json_decode((string) $output);
|
||||
if(isset($output->data)) $output->data = json_decode((string) $output->data);
|
||||
if(isset($output->load))
|
||||
if(isset($output->data) && !is_object($output->data)) $output->data = json_decode((string) $output->data);
|
||||
if(!isset($output->status) && isset($output->result)) $output->status = $output->result;
|
||||
if(isset($output->load->alert))
|
||||
{
|
||||
$output->code = 400;
|
||||
$output->status = 'fail';
|
||||
$output->message = isset($output->load->alert) ? $output->load->alert : zget($output, 'confirm', '');
|
||||
$output->message = $output->load->alert;
|
||||
unset($output->load);
|
||||
}
|
||||
|
||||
|
||||
@@ -3294,7 +3294,7 @@ class baseRouter
|
||||
if(!empty($this->config->debug) && $this->config->debug > 1)
|
||||
{
|
||||
/* Send non-serious errors to page in zin mode. */
|
||||
$isZinRequest = isset($this->config->zin) || isset($_SERVER['HTTP_X_ZIN_OPTIONS']);
|
||||
$isZinRequest = isset($_SERVER['HTTP_X_ZIN_OPTIONS']);
|
||||
$isNonSeriousError = $level !== E_ERROR && $level !== E_PARSE && $level !== E_CORE_ERROR && $level !== E_COMPILE_ERROR;
|
||||
if($isZinRequest && $isNonSeriousError)
|
||||
{
|
||||
|
||||
+20
-15
@@ -44,7 +44,6 @@ class control extends baseControl
|
||||
/* Code for task #9224. Set requiredFields for workflow. */
|
||||
if($this->dbh && ($this->app->isServing() || (defined('RUN_MODE') and RUN_MODE == 'api')))
|
||||
{
|
||||
$this->extendRequireFields();
|
||||
$this->extendExportFields();
|
||||
$this->extendEditorFields();
|
||||
|
||||
@@ -495,7 +494,7 @@ class control extends baseControl
|
||||
{
|
||||
parse_str($extras, $result);
|
||||
$html = $this->flow->buildExtendHtmlValue($object, zget($result, 'position', 'info'));
|
||||
$html .= $this->appendExtendCssAndJS($moduleName, $methodName);
|
||||
$html .= $this->appendExtendCssAndJS($moduleName, $methodName, $object);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -516,10 +515,11 @@ class control extends baseControl
|
||||
* @param zin\fieldList $fields
|
||||
* @param string $moduleName
|
||||
* @param string $methodName
|
||||
* @param object $object
|
||||
* @access public
|
||||
* @return zin\fieldList
|
||||
*/
|
||||
public function appendExtendFields(zin\fieldList $fields, string $moduleName = '', string $methodName = ''): zin\fieldList
|
||||
public function appendExtendFields(zin\fieldList $fields, string $moduleName = '', string $methodName = '', object $object = null): zin\fieldList
|
||||
{
|
||||
if($this->config->edition == 'open') return $fields;
|
||||
if(!empty($this->app->installing) || !empty($this->app->upgrading)) return $fields;
|
||||
@@ -533,7 +533,8 @@ class control extends baseControl
|
||||
$action = $this->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName);
|
||||
if(!$action || $action->extensionType != 'extend') return $fields;
|
||||
|
||||
$fieldList = $this->workflowaction->getFields($flow->module, $action->action);
|
||||
$uiID = $this->loadModel('workflowlayout')->getUIByData($flow->module, $action->action, $object);
|
||||
$fieldList = $this->workflowaction->getFields($flow->module, $action->action, true, null, $uiID);
|
||||
return $this->loadModel('flow')->buildFormFields($fields, $fieldList);
|
||||
}
|
||||
|
||||
@@ -543,10 +544,11 @@ class control extends baseControl
|
||||
*
|
||||
* @param string $moduleName
|
||||
* @param string $methodName
|
||||
* @param object $object
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function appendExtendCssAndJS(string $moduleName = '', string $methodName = ''): string
|
||||
public function appendExtendCssAndJS(string $moduleName = '', string $methodName = '', object $object = null): string
|
||||
{
|
||||
if($this->config->edition == 'open') return '';
|
||||
if(!empty($this->app->installing) || !empty($this->app->upgrading)) return '';
|
||||
@@ -560,7 +562,8 @@ class control extends baseControl
|
||||
$action = $this->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName);
|
||||
if(!$action || $action->extensionType == 'none') return '';
|
||||
|
||||
$fieldList = $this->workflowaction->getFields($flow->module, !empty($action->action) ? $action->action: '');
|
||||
$uiID = $this->loadModel('workflowlayout')->getUIByData($flow->module, !empty($action->action) ? $action->action: '', $object);
|
||||
$fieldList = $this->loadModel('workflowaction')->getFields($flow->module, !empty($action->action) ? $action->action: '', true, null, $uiID);
|
||||
|
||||
$html = '';
|
||||
if(!empty($flow->css)) $html .= "<style>$flow->css</style>";
|
||||
@@ -593,8 +596,6 @@ class control extends baseControl
|
||||
$moduleName = $moduleName ? $moduleName : $this->app->rawModule;
|
||||
$methodName = $methodName ? $moduleName : $this->app->rawMethod;
|
||||
|
||||
if(!$object) $object = new stdclass();
|
||||
|
||||
$this->loadModel('flow');
|
||||
$this->loadModel('workflowfield');
|
||||
|
||||
@@ -604,9 +605,11 @@ class control extends baseControl
|
||||
$action = $this->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName);
|
||||
if(!$action || $action->extensionType != 'extend') return array();
|
||||
|
||||
$uiID = is_object($object) ? $this->loadModel('workflowlayout')->getUIByData($flow->module, $action->action, $object) : 0;
|
||||
|
||||
$wrapControl = array('textarea', 'richtext', 'file');
|
||||
$fieldList = $this->workflowaction->getFields($flow->module, $action->action);
|
||||
$layouts = $this->loadModel('workflowlayout')->getFields($moduleName, $methodName);
|
||||
$fieldList = $this->workflowaction->getFields($flow->module, $action->action, true, $object, $uiID);
|
||||
$layouts = $this->loadModel('workflowlayout')->getFields($moduleName, $methodName, $uiID);
|
||||
$notEmptyRule = $this->loadModel('workflowrule')->getByTypeAndRule('system', 'notempty');
|
||||
|
||||
if($layouts)
|
||||
@@ -622,13 +625,13 @@ class control extends baseControl
|
||||
if($field->buildin || !$field->show || !isset($layouts[$field->field]) || (!empty($field->position) && $position != $field->position)) unset($fieldList[$key]);
|
||||
|
||||
if((is_numeric($field->default) || $field->default) && empty($field->defaultValue)) $field->defaultValue = $field->default;
|
||||
if(empty($object->{$field->field})) $object->{$field->field} = $field->defaultValue;
|
||||
if($object && empty($object->{$field->field})) $object->{$field->field} = $field->defaultValue;
|
||||
if(in_array($field->type, $this->config->workflowfield->numberTypes)) $field = $this->workflowfield->processNumberField($field);
|
||||
|
||||
$field->required = $field->readonly || ($notEmptyRule && strpos(",$field->rules,", ",{$notEmptyRule->id},") !== false);
|
||||
$field->control = $this->flow->buildFormControl($field);
|
||||
$field->items = $field->options ? array_filter($field->options) : null;
|
||||
$field->value = !empty($object) ? zget($object, $field->field, '') : $defaultValue;
|
||||
$field->value = !empty($object) ? zget($object, $field->field, '') : '';
|
||||
$field->width = $field->width != 'auto' ? $field->width : 'full';
|
||||
}
|
||||
|
||||
@@ -684,13 +687,15 @@ class control extends baseControl
|
||||
* 将工作流扩展的必填字段加到requiredFields里,类似批量操作的会在checkFlow之前手动校验。
|
||||
* append workflow extension fields to requiredFields.
|
||||
*
|
||||
* @param int $objectID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function extendRequireFields()
|
||||
public function extendRequireFields(int $objectID = 0)
|
||||
{
|
||||
$fields = $this->loadModel('workflowaction')->getFields($this->moduleName, $this->methodName);
|
||||
$layouts = $this->loadModel('workflowlayout')->getFields($this->moduleName, $this->methodName);
|
||||
$uiID = $this->loadModel('workflowlayout')->getUIByDataID($this->moduleName, $this->methodName, $objectID);
|
||||
$fields = $this->loadModel('workflowaction')->getFields($this->moduleName, $this->methodName, true, null, $uiID);
|
||||
$layouts = $this->loadModel('workflowlayout')->getFields($this->moduleName, $this->methodName, $uiID);
|
||||
$notEmptyRule = $this->loadModel('workflowrule')->getByTypeAndRule('system', 'notempty');
|
||||
foreach($fields as $field)
|
||||
{
|
||||
|
||||
@@ -353,15 +353,16 @@ class model extends baseModel
|
||||
/**
|
||||
* Get flow extend fields.
|
||||
*
|
||||
* @param int $objectID
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getFlowExtendFields()
|
||||
public function getFlowExtendFields(int $objectID = 0): array
|
||||
{
|
||||
if($this->config->edition == 'open') return array();
|
||||
if(!empty($this->app->installing) || !empty($this->app->upgrading)) return array();
|
||||
|
||||
return $this->loadModel('flow')->getExtendFields($this->app->getModuleName(), $this->app->getMethodName());
|
||||
return $this->loadModel('flow')->getExtendFields($this->app->getModuleName(), $this->app->getMethodName(), $objectID);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -398,7 +399,8 @@ class model extends baseModel
|
||||
|
||||
$this->loadModel('file');
|
||||
if($this->post->uid) $this->file->updateObjectID($this->post->uid, $objectID, $moduleName);
|
||||
$fields = $this->workflowaction->getFields($moduleName, $action->action, '');
|
||||
$uiID = $this->loadModel('workflowlayout')->getUIByDataID($moduleName, $methodName, $objectID);
|
||||
$fields = $this->workflowaction->getFields($moduleName, $action->action, '', null, $uiID);
|
||||
foreach($fields as $field)
|
||||
{
|
||||
if($field->control == 'file' && $field->show && !$field->readonly)
|
||||
|
||||
@@ -43,7 +43,7 @@ class date
|
||||
*/
|
||||
public static function today()
|
||||
{
|
||||
return date(DT_DATE2, time());
|
||||
return date(DT_DATE1, time());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+11
-5
@@ -65,15 +65,16 @@ class form extends fixer
|
||||
* Get the form data.
|
||||
*
|
||||
* @param array|null $configObject
|
||||
* @param int $objectID
|
||||
* @return form
|
||||
*/
|
||||
public static function data(array $configObject = null): form
|
||||
public static function data(array $configObject = null, int $objectID = 0): form
|
||||
{
|
||||
global $app, $config;
|
||||
|
||||
$form = new form;
|
||||
if($configObject === null) $configObject = $config->{$app->moduleName}->form->{$app->methodName};
|
||||
$configObject = $form->appendExtendFormConfig($configObject);
|
||||
$configObject = $form->appendExtendFormConfig($configObject, '', '', $objectID);
|
||||
return $form->config($configObject);
|
||||
}
|
||||
|
||||
@@ -102,10 +103,11 @@ class form extends fixer
|
||||
* @param array $config
|
||||
* @param string $moduleName
|
||||
* @param string $methodName
|
||||
* @param int $objectID
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function appendExtendFormConfig(array $configObject, string $moduleName = '', string $methodName = ''): array
|
||||
public function appendExtendFormConfig(array $configObject, string $moduleName = '', string $methodName = '', int $objectID = 0): array
|
||||
{
|
||||
global $app, $config;
|
||||
if($config->edition == 'open' || !empty($app->installing)) return $configObject;
|
||||
@@ -119,8 +121,9 @@ class form extends fixer
|
||||
$action = $app->control->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName);
|
||||
if(!$action || $action->extensionType != 'extend') return $configObject;
|
||||
|
||||
$fieldList = $app->control->workflowaction->getFields($flow->module, $action->action);
|
||||
$layouts = $app->control->loadModel('workflowlayout')->getFields($moduleName, $methodName);
|
||||
$uiID = $app->control->loadModel('workflowlayout')->getUIByDataID($flow->module, $action->action, $objectID);
|
||||
$fieldList = $app->control->workflowaction->getFields($flow->module, $action->action, true, null, $uiID);
|
||||
$layouts = $app->control->workflowlayout->getFields($moduleName, $methodName, $uiID);
|
||||
$notEmptyRule = $app->control->loadModel('workflowrule')->getByTypeAndRule('system', 'notempty');
|
||||
if($layouts)
|
||||
{
|
||||
@@ -195,6 +198,9 @@ class form extends fixer
|
||||
|
||||
$module = $app->getModuleName() == 'story' ? $app->rawModule : $app->getModuleName();
|
||||
$method = $app->getMethodName();
|
||||
if($module == 'feedback' && $app->rawMethod == 'tostory') $module = 'story';
|
||||
if($module == 'feedback' && $app->rawMethod == 'touserstory') $module = 'requirement';
|
||||
if($module == 'feedback' && $app->rawMethod == 'toepic') $module = 'epic';
|
||||
|
||||
if($method == 'batchcreate') $method = 'create';
|
||||
if($method == 'batchedit') $method = 'edit';
|
||||
|
||||
@@ -208,6 +208,8 @@ function thinkCheckbox(): thinkCheckbox {return createWg('thinkCheckbox', func_g
|
||||
function thinkOptions(): thinkOptions{return createWg('thinkOptions', func_get_args());}
|
||||
function thinkTableInput(): thinkTableInput {return createWg('thinkTableInput', func_get_args());}
|
||||
function thinkInput(): thinkInput {return createWg('thinkInput', func_get_args());}
|
||||
function thinkMatrixOptions(): thinkMatrixOptions {return createWg('thinkMatrixOptions', func_get_args());}
|
||||
function thinkMulticolumn(): thinkMulticolumn {return createWg('thinkMulticolumn', func_get_args());}
|
||||
function thinkStepMenu(): thinkStepMenu {return createWg('thinkStepMenu', func_get_args());}
|
||||
function thinkResult(): thinkResult {return createWg('thinkResult', func_get_args());}
|
||||
function thinkSwot(): thinkSwot {return createWg('thinkSwot', func_get_args());}
|
||||
|
||||
@@ -44,8 +44,8 @@ class backBtn extends btn
|
||||
|
||||
$backs = array(
|
||||
'task' => 'execution-task,my-work,my-contribute,execution-tree,execution-grouptask,project-execution,product-track,repo-view,story-change,execution-kanban,execution-taskkanban,my-index,feedback-adminview,projectstory-track,execution-calendar,my-effort,product-dynamic,project-dynamic,execution-dynamic,project-view,execution-view',
|
||||
'story' => 'product-browse,projectstory-story,execution-story,my-work,my-contribute,productplan-view,build-view,projectbuild-view,product-track,repo-view,testcase-zerocase,my-index,feedback-adminview,projectstory-track,task-view,my-effort,demandpool-track,product-dynamic,project-dynamic,execution-dynamic,project-view,execution-view',
|
||||
'bug' => 'bug-browse,project-bug,my-work,my-contribute,execution-bug,bug-view,qa-index,execution-task,product-track,execution-task,task-view,repo-view,story-change,repo-review,feedback-adminview,my-index,projectstory-track,my-effort,product-dynamic,project-dynamic,execution-dynamic,project-view,execution-view',
|
||||
'story' => 'product-browse,projectstory-story,execution-story,my-work,my-contribute,productplan-view,build-view,projectbuild-view,product-track,repo-view,testcase-zerocase,my-index,feedback-adminview,projectstory-track,task-view,my-effort,demandpool-track,product-dynamic,project-dynamic,execution-dynamic,project-view,execution-view,ticket-browse,ticket-view',
|
||||
'bug' => 'bug-browse,project-bug,my-work,my-contribute,execution-bug,bug-view,qa-index,execution-task,product-track,execution-task,task-view,repo-view,story-change,repo-review,feedback-adminview,my-index,projectstory-track,my-effort,product-dynamic,project-dynamic,execution-dynamic,project-view,execution-view,ticket-browse,ticket-view',
|
||||
'testcase' => 'testcase-browse,project-testcase,my-work,my-contribute,execution-testcase,testtask-cases,testsuite-view,product-browse,testcase-view,qa-index,caselib-browse,product-track,story-change,my-index,projectstory-track,my-effort,product-dynamic,project-dynamic,execution-dynamic,project-view,execution-view',
|
||||
'testsuite' => 'testsuite-browse,testsuite-view,testtask-cases,my-index,product-dynamic,project-dynamic,execution-dynamic,project-view,execution-view',
|
||||
'testtask' => 'testtask-browse,testtask-cases,qa-index,testcase-browse,execution-build,my-index,my-effort,product-dynamic,project-dynamic,execution-dynamic,project-view,execution-view',
|
||||
@@ -81,14 +81,16 @@ class backBtn extends btn
|
||||
'risk' => 'risk-browse,risk-view,my-index,project-dynamic,execution-dynamic,project-view,execution-view',
|
||||
'meeting' => 'meeting-browse,meeting-view,my-index,project-dynamic,execution-dynamic,project-view,execution-view',
|
||||
'todo' => 'my-todo,my-effort',
|
||||
'ticket' => 'my-effort',
|
||||
'feedback' => 'my-effort,feedback-admin,feedback-browse,my-work,my-contribute',
|
||||
'ticket' => 'my-effort,ticket-browse,my-work,my-contribute',
|
||||
'designguide' => 'designguide-browse,designguide-view,execution-task,task-view', // For designguide plugin.
|
||||
'deploy' => 'deploy-browse,deploy-steps,deploy-scope,deploy-view,deploy-cases',
|
||||
'service' => 'service-browse,service-manage,service-view',
|
||||
'domain' => 'domain-browse,domain-view',
|
||||
'researchtask' => 'marketresearch-task,my-index,product-dynamic,project-dynamic,project-view',
|
||||
'review' => 'review-browse,my-index,product-dynamic,project-dynamic,project-view',
|
||||
'charter' => 'charter-browse'
|
||||
'charter' => 'charter-browse',
|
||||
'approvalflow' => 'approvalflow-browse'
|
||||
);
|
||||
|
||||
$props = parent::getProps();
|
||||
|
||||
@@ -41,7 +41,7 @@ class demandBasicInfo extends wg
|
||||
zget($demands, $demand->parent),
|
||||
set::href(helper::createLink('demand', 'view', "demandID=$demand->parent")),
|
||||
set::title(zget($demands, $demand->parent)),
|
||||
setClass('basis-32 text-clip mr-2.5'),
|
||||
setClass('basis-52 text-clip mr-2.5'),
|
||||
setData('toggle', 'modal'),
|
||||
setData('size', 'lg')
|
||||
),
|
||||
|
||||
@@ -465,6 +465,7 @@ CSS;
|
||||
|
||||
list($linkCreator, $prevBtn, $nextBtn, $objectType) = $this->prop(array('linkCreator', 'prevBtn', 'nextBtn', 'objectType'));
|
||||
$preAndNext = data('preAndNext');
|
||||
$idKey = isset($preAndNext->idKey) ? $preAndNext->idKey : 'id';
|
||||
|
||||
global $app;
|
||||
if(!$linkCreator && $preAndNext && ($prevBtn === true || $nextBtn === true))
|
||||
@@ -473,9 +474,10 @@ CSS;
|
||||
}
|
||||
if($prevBtn === true && $preAndNext && $preAndNext->pre && $linkCreator)
|
||||
{
|
||||
$prevBtn = array();
|
||||
$prevBtn['url'] = str_replace('{id}', "{$preAndNext->pre->id}", $linkCreator);
|
||||
$prevBtn['hint'] = "#{$preAndNext->pre->id} " . (isset($preAndNext->pre->title) ? $preAndNext->pre->title : $preAndNext->pre->name);
|
||||
$prevBtn = array();
|
||||
$objectID = $preAndNext->pre->$idKey;
|
||||
$prevBtn['url'] = str_replace('{id}', "{$objectID}", $linkCreator);
|
||||
$prevBtn['hint'] = "#{$objectID} " . (isset($preAndNext->pre->title) ? $preAndNext->pre->title : $preAndNext->pre->name);
|
||||
}
|
||||
elseif(is_string($prevBtn))
|
||||
{
|
||||
@@ -483,9 +485,10 @@ CSS;
|
||||
}
|
||||
if($nextBtn === true && $preAndNext && $preAndNext->next && $linkCreator)
|
||||
{
|
||||
$nextBtn = array();
|
||||
$nextBtn['url'] = str_replace('{id}', "{$preAndNext->next->id}", $linkCreator);
|
||||
$nextBtn['hint'] = "#{$preAndNext->next->id} " . (isset($preAndNext->next->title) ? $preAndNext->next->title : $preAndNext->next->name);
|
||||
$nextBtn = array();
|
||||
$objectID = $preAndNext->next->$idKey;
|
||||
$nextBtn['url'] = str_replace('{id}', "{$objectID}", $linkCreator);
|
||||
$nextBtn['hint'] = "#{$objectID} " . (isset($preAndNext->next->title) ? $preAndNext->next->title : $preAndNext->next->name);
|
||||
}
|
||||
elseif(is_string($nextBtn))
|
||||
{
|
||||
@@ -540,7 +543,20 @@ CSS;
|
||||
$this->buildHeader(),
|
||||
$this->buildBody(),
|
||||
$layout === 'simple' ? null : $this->buildPrevAndNext(),
|
||||
html($app->control->appendExtendCssAndJS())
|
||||
html($app->control->appendExtendCssAndJS('', '', $this->prop('object'))),
|
||||
js
|
||||
(
|
||||
'const $doc = $(document)',
|
||||
'const binddedKey = "zt.detail.bindded"',
|
||||
'if($doc.data(binddedKey)) return',
|
||||
'$doc.on("keyup.detail.zt", e => {',
|
||||
'let $btn = null',
|
||||
'if(e.keyCode === 37) $btn = $(".detail-prev-btn")',
|
||||
'else if(e.keyCode === 39) $btn = $(".detail-next-btn")',
|
||||
'if($btn && $btn.length) $btn[0].click()',
|
||||
'}).data(binddedKey, true)',
|
||||
'$doc.one("pageunmount.app", () => {$(document).off("keyup.detail.zt").removeData(binddedKey)})'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ class detailBody extends wg
|
||||
empty($floating) ? null : center(setClass('pt-6 sticky bottom-0'), $floating)
|
||||
),
|
||||
$side,
|
||||
html($app->control->appendExtendCssAndJS())
|
||||
html($app->control->appendExtendCssAndJS('', '', data($app->getModuleName())))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ class detailBody extends wg
|
||||
$side
|
||||
)
|
||||
),
|
||||
html($app->control->appendExtendCssAndJS())
|
||||
html($app->control->appendExtendCssAndJS('', '', data($app->getModuleName())))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,9 +60,9 @@ class detailCard extends wg
|
||||
$objectID = $this->prop('objectID');
|
||||
$object = $this->prop('object');
|
||||
|
||||
if(!$objectType) $objectType = $app->rawModule;
|
||||
if(!$object) $object = data($objectType);
|
||||
if(!$objectID) $objectID = $object ? $object->id : data($objectType . 'ID');
|
||||
if(!$objectType) $objectType = $app->rawModule;
|
||||
if(!$object) $object = data($objectType);
|
||||
if(!$objectID) $objectID = $object ? $object->id : data($objectType . 'ID');
|
||||
|
||||
if(!$this->hasProp('objectType')) $this->setProp('objectType', $objectType);
|
||||
if(!$this->hasProp('objectID')) $this->setProp('objectID', $objectID);
|
||||
|
||||
@@ -79,11 +79,13 @@ class featureBar extends wg
|
||||
|
||||
foreach($moreSelects as $key => $text)
|
||||
{
|
||||
$searchModule = data("config.{$currentModule}.search.module") ? data("config.{$currentModule}.search.module") : $currentModule;
|
||||
|
||||
$subItem = array();
|
||||
$subItem['text'] = $text;
|
||||
$subItem['active'] = $key == $current;
|
||||
$subItem['url'] = ($callback instanceof \Closure) ? $callback($key, $text) : str_replace('{key}', (string)$key, $link);
|
||||
$subItem['attrs'] = ['data-id' => $key, 'data-load' => $load, 'data-target' => $loadID, 'data-app' => $tab];
|
||||
$subItem['attrs'] = ['data-id' => $key, 'data-load' => $load, 'data-target' => $loadID, 'data-app' => $tab, 'data-success' => "() => zui.updateSearchForm('$searchModule')"];
|
||||
|
||||
if($item->name == 'QUERY')
|
||||
{
|
||||
|
||||
@@ -73,13 +73,21 @@ class formPanel extends panel
|
||||
return file_get_contents(__DIR__ . DS . 'js' . DS . 'v1.js');
|
||||
}
|
||||
|
||||
protected function getData()
|
||||
{
|
||||
global $app;
|
||||
$moduleName = $app->getModuleName();
|
||||
if($moduleName == 'caselib') $moduleName = 'lib';
|
||||
return data($moduleName);
|
||||
}
|
||||
|
||||
protected function created()
|
||||
{
|
||||
$fields = $this->prop('fields');
|
||||
if(is_object($fields))
|
||||
{
|
||||
global $app;
|
||||
$fields = $app->control->appendExtendFields($fields);
|
||||
$fields = $app->control->appendExtendFields($fields, '', '', $this->getData());
|
||||
$this->setProp('fields', $fields);
|
||||
}
|
||||
|
||||
@@ -171,9 +179,7 @@ class formPanel extends panel
|
||||
$layout = $this->prop('layout');
|
||||
if($layout == 'grid') return null;
|
||||
|
||||
$moduleName = $app->getModuleName();
|
||||
if($moduleName == 'caselib') $moduleName = 'lib';
|
||||
$data = data($moduleName);
|
||||
$data = $this->getData();
|
||||
$fields = $app->control->appendExtendForm('info', $data);
|
||||
$extraMain = array();
|
||||
foreach($fields as $field)
|
||||
@@ -206,7 +212,7 @@ class formPanel extends panel
|
||||
{
|
||||
global $app;
|
||||
|
||||
$data = data($app->getModuleName());
|
||||
$data = $this->getData();
|
||||
$fields = $app->control->appendExtendForm('info', $data);
|
||||
|
||||
$formBatchItem = array();
|
||||
@@ -309,7 +315,7 @@ class formPanel extends panel
|
||||
setClass('panel-body ' . $this->prop('bodyClass')),
|
||||
set($this->prop('bodyProps')),
|
||||
$this->buildContainer($this->buildForm()),
|
||||
html($app->control->appendExtendCssAndJS())
|
||||
html($app->control->appendExtendCssAndJS('', '', $this->getData()))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -483,11 +483,7 @@ function initGantt()
|
||||
|
||||
gantt.templates.tooltip_text = function (start, end, task)
|
||||
{
|
||||
if(task.type == 'point') return '';
|
||||
|
||||
let $text = $(task.text);
|
||||
if($text.length == 0) return task.text;
|
||||
if($text[2]) return $text[2].innerHTML;
|
||||
return task.text;
|
||||
};
|
||||
|
||||
gantt.templates.task_end_date = function(data)
|
||||
@@ -590,7 +586,7 @@ function initGantt()
|
||||
|
||||
if(editBtn.hasClass('icon-confirm'))
|
||||
{
|
||||
let pointAttr = JSON.parse(reviewPoints);
|
||||
let pointAttr = reviewPoints;
|
||||
let category = id.split("-")[1];
|
||||
|
||||
if(pointAttr[category]['disabled'])
|
||||
|
||||
@@ -81,7 +81,7 @@ class header extends wg
|
||||
);
|
||||
}
|
||||
|
||||
static function userBar()
|
||||
public static function userBar()
|
||||
{
|
||||
global $lang, $app, $config;
|
||||
|
||||
@@ -238,7 +238,6 @@ class header extends wg
|
||||
),
|
||||
|
||||
set::id('userMenu'),
|
||||
set::trigger('hover'),
|
||||
set::placement('bottom-end'),
|
||||
set::menu(array('style' => array('color' => 'var(--color-fore)'))),
|
||||
set::strategy('fixed'),
|
||||
@@ -247,7 +246,7 @@ class header extends wg
|
||||
);
|
||||
}
|
||||
|
||||
static function messageBar()
|
||||
public static function messageBar()
|
||||
{
|
||||
global $app, $lang, $config;
|
||||
|
||||
@@ -287,7 +286,7 @@ class header extends wg
|
||||
);
|
||||
}
|
||||
|
||||
static function quickAddMenu()
|
||||
public static function quickAddMenu()
|
||||
{
|
||||
global $app, $config, $lang;
|
||||
|
||||
@@ -462,7 +461,6 @@ class header extends wg
|
||||
|
||||
set::id('quickAddMenu'),
|
||||
set::menu(array('style' => array('color' => 'var(--color-fore)'))),
|
||||
set::trigger('hover'),
|
||||
set::placement('bottom'),
|
||||
set::strategy('fixed'),
|
||||
set::arrow(true),
|
||||
|
||||
@@ -75,6 +75,7 @@ class heading extends wg
|
||||
if($app->tab == 'qa') $module = 'product';
|
||||
if($app->tab == 'bi') $module = 'dimension';
|
||||
|
||||
if($app->tab == 'feedback' && $moduleName == 'ticket') $module = 'product';
|
||||
if($app->tab == 'qa' && ($moduleName == 'caselib' || data('isLibCase'))) $module = 'caselib';
|
||||
return new dropmenu(set::tab($module));
|
||||
}
|
||||
|
||||
@@ -138,7 +138,62 @@ class mainNavbar extends nav
|
||||
if($itemProps) $item = array_merge($item, $itemProps);
|
||||
if(is_callable($onRenderItem)) $item = $onRenderItem($item, $menuItem);
|
||||
|
||||
$items[] = $item;
|
||||
if(isset($menuItem['dropMenu']))
|
||||
{
|
||||
$dropItems = array();
|
||||
$label = $menuItem['text'];
|
||||
$menuActive = '';
|
||||
foreach($menuItem['dropMenu'] as $dropMenuKey => $dropMenuItem)
|
||||
{
|
||||
if(isset($dropMenuItem['hidden']) && $dropMenuItem['hidden']) continue;
|
||||
|
||||
if(empty($dropMenuItem['alias'])) $dropMenuItem['alias'] = '';
|
||||
if(empty($dropMenuItem['exclude'])) $dropMenuItem['exclude'] = '';
|
||||
|
||||
list($dropMenuName, $dropMenuModule, $dropMenuMethod, $dropMenuParams) = explode('|', $dropMenuItem['link']);
|
||||
$dropActive = '';
|
||||
$dropModule = isset($dropMenuModule) ? $dropMenuModule : '';
|
||||
$dropMethod = isset($dropMenuMethod) ? $dropMenuMethod : '';
|
||||
$dropParams = isset($dropMenuParams) ? $dropMenuParams : '';
|
||||
$dropLabel = isset($dropMenuName) ? $dropMenuName : '';
|
||||
$dropLink = helper::createLink($dropModule, $dropMethod, $dropParams);
|
||||
|
||||
$subModule = isset($dropMenuItem['subModule']) ? explode(',', $dropMenuItem['subModule']) : array();
|
||||
if($subModule && in_array($currentModule, $subModule)) $dropActive = 'active';
|
||||
if($dropModule == $currentModule && $dropMethod == $currentMethod) $dropActive = 'active';
|
||||
if($dropModule == $currentModule && strpos(",{$dropMenuItem['alias']},", ",{$currentMethod},") !== false) $dropActive = 'active';
|
||||
if(strpos(",{$dropMenuItem['exclude']},", ",{$currentModule}-{$currentMethod},") !== false || strpos(",{$dropMenuItem['exclude']},", ",{$currentModule},") !== false) $dropActive = '';
|
||||
if($dropActive)
|
||||
{
|
||||
$label = $dropLabel;
|
||||
$menuActive = 'active';
|
||||
}
|
||||
|
||||
$dropItems[] = array(
|
||||
'active' => $dropActive ? true : false,
|
||||
'data-id' => $dropMenuKey,
|
||||
'url' => $dropLink,
|
||||
'text' => $dropLabel,
|
||||
'data-app' => $app->tab
|
||||
);
|
||||
}
|
||||
|
||||
if(empty($dropItems)) continue;
|
||||
$items[] = array
|
||||
(
|
||||
'type' => 'dropdown',
|
||||
'items' => $dropItems,
|
||||
'text' => $label,
|
||||
'data-id' => $name,
|
||||
'data-app' => $app->tab,
|
||||
'trigger' => 'hover',
|
||||
'class' => $menuActive
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
$items[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
jsVar('allMainNavbarItems', $items);
|
||||
|
||||
@@ -50,9 +50,10 @@ class modalHeader extends wg
|
||||
($entityText || $entityID) ? entityLabel
|
||||
(
|
||||
set::level(1),
|
||||
setClass('pl-2'),
|
||||
setClass('pl-2 flex'),
|
||||
$entityText ? set::text($entityText) : null,
|
||||
$entityID ? set::entityID($entityID) : null,
|
||||
$entityID ? set::idClass('self-center') : null,
|
||||
set::reverse(true)
|
||||
) : null,
|
||||
$this->block('suffix')
|
||||
|
||||
@@ -91,7 +91,7 @@ class moduleMenu extends wg
|
||||
);
|
||||
$items = $this->buildMenuTree($child->id);
|
||||
if($items) $item['items'] = $items;
|
||||
if($child->id == $activeKey)
|
||||
if($child->id == $activeKey || $child->id == 'product-' . $activeKey)
|
||||
{
|
||||
$itemKey = $this->prop('checkbox') ? 'checked' : 'selected';
|
||||
$item[$itemKey] = true;
|
||||
@@ -129,7 +129,7 @@ class moduleMenu extends wg
|
||||
|
||||
$modules = $this->prop('modules');
|
||||
$moduleName = '';
|
||||
if($modules) array_map(function($module) use(&$moduleName, $activeKey) { if($module->id == $activeKey) $moduleName = $module->name; }, $modules);
|
||||
if($modules) array_map(function($module) use(&$moduleName, $activeKey) { if($module->id == $activeKey || $module->id == 'product-' . $activeKey) $moduleName = $module->name; }, $modules);
|
||||
|
||||
return $moduleName;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ function createMonaco(id, action, options, diffContent, onMouseDown, onMouseMove
|
||||
|
||||
options.lineNumbers = function(number)
|
||||
{
|
||||
var newlc = diffContent.line.new;
|
||||
const newlc = diffContent.line.new;
|
||||
return newlc[number - 1];
|
||||
};
|
||||
|
||||
@@ -32,13 +32,20 @@ function createMonaco(id, action, options, diffContent, onMouseDown, onMouseMove
|
||||
window.modifiedEditor = modifiedEditor;
|
||||
|
||||
modifiedEditor.setModel({
|
||||
original: monaco.editor.createModel(diffContent.code.old.trim("\n"), options.lang),
|
||||
modified: monaco.editor.createModel(diffContent.code.new.trim("\n"), options.lang),
|
||||
original: monaco.editor.createModel(diffContent.code.old, options.lang),
|
||||
modified: monaco.editor.createModel(diffContent.code.new, options.lang),
|
||||
});
|
||||
|
||||
editor = modifiedEditor.getModifiedEditor();
|
||||
|
||||
var getOriginalEditor = modifiedEditor.getOriginalEditor();
|
||||
const getOriginalEditor = modifiedEditor.getOriginalEditor();
|
||||
getOriginalEditor.updateOptions({
|
||||
lineNumbers: function(number)
|
||||
{
|
||||
var oldlc = diffContent.line.old;
|
||||
return oldlc[number - 1];
|
||||
}
|
||||
});
|
||||
|
||||
if(onMouseDown) getOriginalEditor.onMouseDown(function(obj){eval(onMouseDown + '(obj)')})
|
||||
if(onMouseMove) getOriginalEditor.onMouseMove(function(obj){eval(onMouseMove + '(obj)')})
|
||||
|
||||
@@ -12,6 +12,7 @@ class panel extends wg
|
||||
'size?: "sm"|"lg"', // 额外尺寸。
|
||||
'title?: string', // 标题。
|
||||
'shadow?: bool=true', // 阴影效果。
|
||||
'titleIcon?: string', // 标题图标。
|
||||
'titleClass?: string', // 标题类名。
|
||||
'titleProps?: array', // 标题属性。
|
||||
'headingClass?: string', // 标题栏类名。
|
||||
|
||||
@@ -190,6 +190,7 @@ class productsBox extends wg
|
||||
set::className('planBox'),
|
||||
picker
|
||||
(
|
||||
set::className('pr-2.5'),
|
||||
set::name("plans[{$planProductID}][]"),
|
||||
set::items(!empty($productPlans) ? $productPlans : array()),
|
||||
set::value($currentPlan),
|
||||
|
||||
@@ -29,6 +29,8 @@ class searchToggle extends wg
|
||||
|
||||
protected function build()
|
||||
{
|
||||
if(!common::hasPriv('search', 'buildForm')) return;
|
||||
|
||||
global $lang, $app, $config;
|
||||
list($target, $module, $open, $url, $searchUrl, $text) = $this->prop(array('target', 'module', 'open', 'url', 'searchUrl', 'text'));
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ class storyBasicInfo extends wg
|
||||
$demand->title,
|
||||
set::href(helper::createLink('demand', 'view', "demandID=$story->demand")),
|
||||
set::title($demand->title),
|
||||
setClass('basis-32 text-clip mr-2.5'),
|
||||
setClass('basis-52 text-clip mr-2.5'),
|
||||
setData('toggle', 'modal'),
|
||||
setData('size', 'lg')
|
||||
) : $demand->title,
|
||||
@@ -142,7 +142,7 @@ class storyBasicInfo extends wg
|
||||
$story->parentName,
|
||||
set::href(helper::createLink($story->parentType, 'view', "storyID=$story->parent")),
|
||||
set::title($story->parentName),
|
||||
setClass('basis-32 text-clip mr-2.5'),
|
||||
setClass('basis-52 text-clip mr-2.5'),
|
||||
setData('toggle', 'modal'),
|
||||
setData('size', 'lg')
|
||||
),
|
||||
@@ -208,7 +208,7 @@ class storyBasicInfo extends wg
|
||||
$items[$lang->story->source] = array
|
||||
(
|
||||
'control' => 'text',
|
||||
'content' => zget($lang->story->sourceList, $story->source, ''),
|
||||
'content' => zget($lang->{$story->type}->sourceList, $story->source, ''),
|
||||
'id' => 'sourceBox'
|
||||
);
|
||||
$items[$lang->story->sourceNote] = array
|
||||
@@ -230,12 +230,12 @@ class storyBasicInfo extends wg
|
||||
'class' => 'stage-line',
|
||||
'text' => zget($lang->{$story->type}->stageList, $this->getMinStage($story, $branches), '')
|
||||
);
|
||||
$items[$lang->story->category] = zget($lang->story->categoryList, $story->category);
|
||||
$items[$lang->story->category] = zget($lang->{$story->type}->categoryList, $story->category);
|
||||
$items[$lang->story->pri] = array
|
||||
(
|
||||
'control' => 'pri',
|
||||
'pri' => $story->pri,
|
||||
'text' => $lang->story->priList
|
||||
'text' => $lang->{$story->type}->priList
|
||||
);
|
||||
$items[$lang->story->estimate] = $story->estimate . $config->hourUnit;
|
||||
if(in_array($story->source, $config->story->feedbackSource))
|
||||
|
||||
@@ -41,6 +41,7 @@ class thinkBaseCheckbox extends wg
|
||||
global $lang;
|
||||
|
||||
if($item instanceof item) $item = $item->props->toJSON();
|
||||
$disabled = $this->prop('disabled');
|
||||
|
||||
if(!isset($item['checked']))
|
||||
{
|
||||
@@ -65,7 +66,7 @@ class thinkBaseCheckbox extends wg
|
||||
(
|
||||
setClass('item-control has-input w-full py-2 px-3 flex gap-3 items-center justify-between border cursor-pointer ' . $itemClass),
|
||||
setData('type', $this->prop('type')),
|
||||
on::click('toggleChecked'),
|
||||
!$disabled ? on::click('toggleChecked') : null,
|
||||
div
|
||||
(
|
||||
setClass('flex items-start text-md gap-1.5 flex-1'),
|
||||
@@ -95,7 +96,7 @@ class thinkBaseCheckbox extends wg
|
||||
return div
|
||||
(
|
||||
setData('type', $this->prop('type')),
|
||||
on::click('toggleChecked'),
|
||||
!$disabled ? on::click('toggleChecked') : null,
|
||||
setClass('item-control w-full py-2 px-3 flex gap-3 items-center justify-between border cursor-pointer ' . $itemClass),
|
||||
div(setClass('text-md flex-1 break-all'), $text),
|
||||
new checkbox
|
||||
@@ -127,7 +128,7 @@ class thinkBaseCheckbox extends wg
|
||||
|
||||
if(!is_array($item)) $item = array('text' => $item, 'value' => $key);
|
||||
if(!isset($item['checked'])) $item['checked'] = !empty($item['value']) && in_array($item['value'], $valueList);
|
||||
$item['text'] = $prefix . '. ' . $item['text'];
|
||||
$item['text'] = !empty($item['disabledPrefix']) ? $item['text'] : $prefix . '. ' . $item['text'];
|
||||
$items[$key] = $this->onBuildItem($item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,12 +22,13 @@ class thinkCheckbox extends thinkRadio
|
||||
$app->loadLang('thinkstep');
|
||||
$formItems = parent::buildFormItem();
|
||||
|
||||
list($step, $minCount, $maxCount, $required) = $this->prop(array('step', 'minCount', 'maxCount', 'required'));
|
||||
list($step, $minCount, $maxCount, $required, $setOption) = $this->prop(array('step', 'minCount', 'maxCount', 'required', 'setOption'));
|
||||
if($step)
|
||||
{
|
||||
$required = $step->options->required;
|
||||
$minCount = $step->options->minCount ?? null;
|
||||
$maxCount = $step->options->maxCount ?? null;
|
||||
$required = $step->options->required;
|
||||
$minCount = $step->options->minCount ?? null;
|
||||
$maxCount = $step->options->maxCount ?? null;
|
||||
$setOption = !empty($step->options->setOption) ? $step->options->setOption : 0;
|
||||
}
|
||||
$className = 'selectable-rows' . (empty($required) ? ' hidden' : '');
|
||||
|
||||
@@ -38,7 +39,7 @@ class thinkCheckbox extends thinkRadio
|
||||
(
|
||||
set::label($lang->thinkstep->label->minCount),
|
||||
set::labelClass('required'),
|
||||
setClass($className),
|
||||
setClass($className, 'min-count'),
|
||||
input
|
||||
(
|
||||
set::placeholder($lang->thinkstep->placeholder->inputContent),
|
||||
@@ -46,20 +47,22 @@ class thinkCheckbox extends thinkRadio
|
||||
set::min(1),
|
||||
set::name('options[minCount]'),
|
||||
set::value($minCount),
|
||||
set::disabled($setOption)
|
||||
),
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
set::label($lang->thinkstep->label->maxCount),
|
||||
set::labelClass('required'),
|
||||
setClass($className),
|
||||
setClass($className, 'max-count'),
|
||||
input
|
||||
(
|
||||
set::placeholder($lang->thinkstep->placeholder->inputContent),
|
||||
set::placeholder($setOption ? $lang->thinkstep->placeholder->maxCount : $lang->thinkstep->placeholder->inputContent),
|
||||
set::type('number'),
|
||||
set::min(1),
|
||||
set::name('options[maxCount]'),
|
||||
set::value($maxCount)
|
||||
set::value($maxCount),
|
||||
set::disabled($setOption)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
.think-multiple {--tw-ring-color: rgba(var(--color-gray-300-rgb), .7); box-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);}
|
||||
.think-multiple-body::-webkit-scrollbar {height: 5px;}
|
||||
.think-multiple-body::-webkit-scrollbar-track {background: var(--color-gray-200);}
|
||||
.think-multiple-item {min-width: 126px; position: relative; display: flex; border-radius: var(--radius); align-items: stretch; min-height: 80px; --tw-ring-color: rgba(var(--color-gray-300-rgb), .7); box-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); margin-top: 1px; z-index: 1;}
|
||||
.think-multiple-item.is-focus {z-index: 100;}
|
||||
.think-multiple-action {opacity: 0; height: clac(8 * var(--space)); width: calc(6 * var(--space)); min-width: calc(6 * var(--space)); max-width: calc(6 * var(--space)); display: flex; justify-content: center; color: var(--color-gray-600);}
|
||||
.think-multiple-item:hover .think-multiple-action {opacity: 1;}
|
||||
.think-multiple-item-move {cursor: move; padding-top: 10px;}
|
||||
.think-multiple-col-delete {padding-top: 4px;}
|
||||
.think-multiple-action .icon {font-size: 12px;}
|
||||
.think-multiple-item-text::placeholder {color: var(--color-gray-400);}
|
||||
.think-multiple .think-multiple-action button {width: calc(5 * var(--space));}
|
||||
.think-multiple .btn-action:hover {color: var(--color-primary-500);}
|
||||
.think-multiple-item .form-control {background: inherit;}
|
||||
.think-multiple-item {background: var(--color-canvas);}
|
||||
.think-multiple-item[data-level="1"] {margin-top: 0;}
|
||||
.think-multiple-drag-ghost {position: absolute; left: 0; top: 0; bottom: 0; opacity: 0; right: 0; pointer-events: none;}
|
||||
.think-multiple-item-move:hover {color: var(--color-primary-500);}
|
||||
.has-dragging .think-multiple-item-move > .icon-move {display: none;}
|
||||
.think-multiple-item.move-hover {background-color: rgba(var(--color-primary-100-rgb),.2);}
|
||||
.think-multiple-item.is-dragging {background-color: rgba(var(--color-primary-100-rgb),.4);}
|
||||
.think-multiple-item.is-sub-dragging {background-color: rgba(var(--color-primary-100-rgb),.2);}
|
||||
.think-multiple-item.is-dropping {z-index: 5;}
|
||||
.think-multiple-item.is-dropping::before,
|
||||
.think-multiple-item.is-dropping::after {content: ' '; display: block; position: absolute; top: 0; z-index: 110;}
|
||||
.think-multiple-item.is-dropping::after {transform: translateX(-2px);}
|
||||
.think-multiple-item.is-dropping::before {width: 2px; height: 100%; background: var(--color-primary-500); top: 0;}
|
||||
.think-multiple-item.is-dropping[data-drop-side="left"]::before {left: 0;}
|
||||
.think-multiple-item.is-dropping[data-drop-side="right"]::before {right: 0;}
|
||||
.think-multiple-item.is-dropping[data-drop-side="left"]::after {left: 0;}
|
||||
.think-multiple-item.is-dropping[data-drop-side="right"]::after {right: -4px;}
|
||||
.think-multiple-item.is-dropping::after {width: 6px; height: 6px; border-radius: 50%; background: var(--color-primary-500); border: var(--color-primary-100) 3px solid; z-index: 120;}
|
||||
.think-multiple-item.is-invalid-drop-level.is-dropping::before,
|
||||
.think-multiple-item.is-invalid-drop-level.is-dropping::after {filter: grayscale(1); opacity: .5;}
|
||||
.think-multiple .think-multiple-buttons { width: 100%; position: absolute; bottom: -20px; right: 0; text-align: right;}
|
||||
@@ -0,0 +1,409 @@
|
||||
function updateItemName(item, name)
|
||||
{
|
||||
if(name === undefined) name = item.name;
|
||||
if(item.infoName === name) return;
|
||||
item.infoName = name;
|
||||
item.name = name;
|
||||
const namePath = name.split('.');
|
||||
item.order = namePath.reduce((total, level, index) => total + (+level * ([1000000, 1000, 1, 0.001, 0.000001][index])), 0);
|
||||
item.level = namePath.length;
|
||||
item.selfName = namePath.pop();
|
||||
item.parentName = namePath.join('.');
|
||||
}
|
||||
|
||||
function updateChildrenName(item)
|
||||
{
|
||||
if(!item.children) return;
|
||||
item.children.forEach((subItem, subIndex) =>
|
||||
{
|
||||
updateItemName(subItem, `${item.name}.${subIndex + 1}`);
|
||||
updateChildrenName(subItem);
|
||||
});
|
||||
}
|
||||
|
||||
/** ThinkMatrixOptions component. */
|
||||
class ThinkMatrixOptions extends zui.Component
|
||||
{
|
||||
static NAME = 'ThinkMatrixOptions';
|
||||
|
||||
static DEFAULT =
|
||||
{
|
||||
colName: 'colFields',
|
||||
cols: ['', '', '', ''],
|
||||
moveIcon: 'move',
|
||||
deleteIcon: 'close',
|
||||
deleteColTip: '',
|
||||
addColTip: '',
|
||||
changeLevelByDrag: false,
|
||||
colPlaceholder: '',
|
||||
addColText: '',
|
||||
isSaveData: true,
|
||||
};
|
||||
|
||||
init()
|
||||
{
|
||||
this.reset(this.options.cols, true);
|
||||
}
|
||||
|
||||
afterInit()
|
||||
{
|
||||
this.render();
|
||||
const $element = this.$element;
|
||||
$element.on('focus', 'input', e =>
|
||||
{
|
||||
const $input = $(e.target);
|
||||
$input.closest('.think-multiple-item').addClass('is-focus');
|
||||
})
|
||||
.on('blur', 'input', e =>
|
||||
{
|
||||
const $input = $(e.target);
|
||||
$input.closest('.think-multiple-item').removeClass('is-focus');
|
||||
this.saveData();
|
||||
})
|
||||
.on('input', 'input', e => {this.saveData();})
|
||||
.on('click', '.btn-action', e =>
|
||||
{
|
||||
const $btn = $(e.currentTarget);
|
||||
if($btn.is('.disabled')) return;
|
||||
|
||||
const action = $btn.attr('data-action');
|
||||
const name = $btn.closest('.think-multiple-item').attr('data-name');
|
||||
if(action === 'delete') this.deleteItem(name);
|
||||
else if(action === 'sib') this.addSib();
|
||||
}).on('mouseenter', '.think-multiple-item-move', e =>
|
||||
{
|
||||
$element.find('.move-hover').removeClass('move-hover');
|
||||
$(e.currentTarget).closest('.think-multiple-item').addClass('move-hover');
|
||||
}).on('mouseleave', '.think-multiple-item-move', e =>
|
||||
{
|
||||
$element.find('.move-hover').removeClass('move-hover');
|
||||
});
|
||||
|
||||
$element.draggable(
|
||||
{
|
||||
selector: '.think-multiple-item',
|
||||
handle: '.think-multiple-item-move',
|
||||
beforeDrag: (_event, dragElement) =>
|
||||
{
|
||||
const dragName = $(dragElement).attr('data-name');
|
||||
this.dragName = dragName;
|
||||
this.dragItem = this.getByName(dragName);
|
||||
this.dragMaxLevel = this.dragItem.level;
|
||||
this.isValidLevel = true;
|
||||
$element.find(`.think-multiple-item[data-name^="${dragName}."]`).addClass('is-sub-dragging').each((_index, ele) =>
|
||||
{
|
||||
const level = +($(ele).attr('data-level'));
|
||||
if(level > this.dragMaxLevel) this.dragMaxLevel = level;
|
||||
});
|
||||
$element.removeClass('cursor-not-allowed');
|
||||
},
|
||||
onDragStart: (event, dragElement) =>
|
||||
{
|
||||
event.dataTransfer.setDragImage($(dragElement).find('.think-multiple-drag-ghost')[0], 0, 0);
|
||||
},
|
||||
onDragEnd: () =>
|
||||
{
|
||||
$element.find('.is-sub-dragging').removeClass('is-sub-dragging');
|
||||
const dropName = this.dropName;
|
||||
if(dropName)
|
||||
{
|
||||
this.dropName = null;
|
||||
const $dropRow = this.getRow(dropName);
|
||||
if($dropRow && $dropRow.length) $dropRow.removeAttr('data-drop-side').removeAttr('data-drop-level');
|
||||
}
|
||||
|
||||
if(!this.isValidLevel) return;
|
||||
const dragName = this.dragName;
|
||||
if(!dropName || dragName === dropName) return;
|
||||
if(this.dropSide === 'right') this.moveAfter(dragName, dropName);
|
||||
else this.moveBefore(dragName, dropName);
|
||||
this.saveData(true);
|
||||
},
|
||||
onDragOver: (event, dragElement, dropElement) =>
|
||||
{
|
||||
const $col = $(dropElement);
|
||||
const dropName = $col.attr('data-name');
|
||||
let idDiff = false;
|
||||
if(dropName !== this.dropName)
|
||||
{
|
||||
this.dropName = dropName;
|
||||
const $oldRow = this.getRow(dropName);
|
||||
$oldRow.removeAttr('data-drop-side')
|
||||
.removeAttr('data-drop-level')
|
||||
.removeAttr('data-invalid-nested');
|
||||
this.dropSide = '';
|
||||
this.dropLevel = '';
|
||||
this.isValidLevel = undefined;
|
||||
idDiff = true;
|
||||
}
|
||||
const dropItem = this.getByName(dropName);
|
||||
const dropBounding = dropElement.getBoundingClientRect();
|
||||
const dropSide = event.clientX > (dropBounding.left + dropBounding.width / 2) ? 'right' : 'left';
|
||||
const dropLevel = Math.max(1, Math.min(4, (!this.options.changeLevelByDrag || event.clientX <= (dropBounding.left + 24)) ? dropItem.level : (Math.round((event.clientX - dropBounding.left - 24 - 8) / 14))));
|
||||
const isValidLevel = (this.dragMaxLevel + (dropLevel - this.dragItem.level)) <= 4;
|
||||
if(this.isValidLevel !== isValidLevel)
|
||||
{
|
||||
this.isValidLevel = isValidLevel;
|
||||
$col.toggleClass('is-invalid-drop-level', !isValidLevel);
|
||||
}
|
||||
$element.toggleClass('cursor-not-allowed', !isValidLevel);
|
||||
if(this.dropSide !== dropSide)
|
||||
{
|
||||
this.dropSide = dropSide;
|
||||
$col.attr('data-drop-side', dropSide);
|
||||
idDiff = true;
|
||||
}
|
||||
if(this.dropLevel !== dropLevel)
|
||||
{
|
||||
this.dropLevel = dropLevel;
|
||||
$col.attr('data-drop-level', dropLevel);
|
||||
idDiff = true;
|
||||
}
|
||||
},
|
||||
target: (dragElement) =>
|
||||
{
|
||||
return $element.find('.think-multiple-item').not('.is-sub-dragging').not(dragElement);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
saveData(isClear = false)
|
||||
{
|
||||
if(!this.options.isSaveData) return;
|
||||
|
||||
const cols = [];
|
||||
$('.think-multiple-body .think-multiple-item-text').each(function()
|
||||
{
|
||||
if($(this).val())
|
||||
{
|
||||
const item = {value: $(this).closest('.think-multiple-item').data('name'), text: $(this).val()};
|
||||
cols.push(item);
|
||||
}
|
||||
});
|
||||
$('.required-options .picker-box').zui('picker').render({items: cols});
|
||||
if(isClear) $('.required-options .picker-box').zui('picker').$.setValue('');
|
||||
}
|
||||
|
||||
getByID(id)
|
||||
{
|
||||
return this._items.find(x => x.id === id);
|
||||
}
|
||||
|
||||
getByName(name)
|
||||
{
|
||||
return this._items.find(x => x.name === name);
|
||||
}
|
||||
|
||||
getParent(item)
|
||||
{
|
||||
if(typeof item === 'string') item = this.getByName(item);
|
||||
return (item && item.parentName) ? this.getByName(item.parentName) : null;
|
||||
}
|
||||
|
||||
getRow(name)
|
||||
{
|
||||
return this.$element.find(`.think-multiple-item[data-name="${name}"]`);
|
||||
}
|
||||
|
||||
reset(data, skipRender)
|
||||
{
|
||||
this._items = [];
|
||||
this.update(data, skipRender);
|
||||
}
|
||||
|
||||
update(data, skipRender)
|
||||
{
|
||||
data.forEach(item =>{this.updateItem(item, true);});
|
||||
if(!skipRender) this.render();
|
||||
}
|
||||
|
||||
updateItem(item, skipRender)
|
||||
{
|
||||
item = typeof item === 'string' ? {name: item, id: $.guid++, step: ''} : $.extend({id: $.guid++, step: ''}, item);
|
||||
updateItemName(item);
|
||||
|
||||
const index = this._items.findIndex(x => x.id === item.id);
|
||||
if(index >= 0)
|
||||
{
|
||||
const oldItem = this._items[index];
|
||||
const parent = this.getParent(oldItem);
|
||||
if(parent) parent.children.splice(parent.children.indexOf(oldItem), 1);
|
||||
item = $.extend(oldItem, item);
|
||||
this._items.splice(index, 1);
|
||||
updateChildrenName(item);
|
||||
}
|
||||
if(item.level > 1)
|
||||
{
|
||||
this._items.push(item);
|
||||
const parent = this._ensureItem(item.parentName);
|
||||
const siblings = parent.children || [];
|
||||
const oldIndex = siblings.findIndex(x => x.id === item.id);
|
||||
if(oldIndex >= 0) siblings.splice(oldIndex, 1);
|
||||
const index = siblings.findIndex(x => x.order > item.order);
|
||||
if(index === 0) siblings.unshift(item);
|
||||
else if(index < 0) siblings.push(item);
|
||||
else if(index > 0) siblings.splice(index, 0, item);
|
||||
parent.children = siblings;
|
||||
}
|
||||
else
|
||||
{
|
||||
const siblings = this._items;
|
||||
const index = siblings.findIndex(x => x.level === 1 && x.order > item.order);
|
||||
if(index === 0) siblings.unshift(item);
|
||||
else if(index < 0) siblings.push(item);
|
||||
else if(index > 0) siblings.splice(index, 0, item);
|
||||
}
|
||||
|
||||
if(!skipRender) this.render();
|
||||
return item;
|
||||
}
|
||||
|
||||
_ensureItem(name)
|
||||
{
|
||||
return this.getByName(name) || this.updateItem(name);
|
||||
}
|
||||
|
||||
_createCol(item, options)
|
||||
{
|
||||
const name = options.cols[item.index] || '';
|
||||
const $col = $
|
||||
([
|
||||
`<div class="think-multiple-item flex-1 py-1.5" data-id="${item.id}">`,
|
||||
'<div class="think-multiple-drag-ghost"></div>',
|
||||
'<div class="think-multiple-item-move think-multiple-action"><i class="icon icon-move"></i></div>',
|
||||
'<div class="flex-1">',
|
||||
'<div class="form-control">',
|
||||
`<input class="think-multiple-item-text w-full h-full" placeholder="${options.colPlaceholder}${item.name}" value="${name}" />`,
|
||||
'</div>',
|
||||
'<div class="mt-2 rounded ring bg-gray-100" style="height: 30px; --tw-ring-color: var(--form-control-border);"></div>',
|
||||
'</div>',
|
||||
'<div class="think-multiple-col-delete think-multiple-action">',
|
||||
`<button type="button" class="btn ghost rounded size-sm square btn-action border-none" data-action="delete"><i class="icon icon-${options.deleteIcon}"></i></button>`,
|
||||
'</div>',
|
||||
'</div>'
|
||||
].join(''));
|
||||
return $col;
|
||||
}
|
||||
|
||||
_renderCol(item, $preCol, $list, $cols)
|
||||
{
|
||||
const options = this.options;
|
||||
let $col = $cols.filter(`[data-id="${item.id}"]`);
|
||||
if(!$col.length) $col = this._createCol(item, options);
|
||||
|
||||
if($preCol) $col.insertAfter($preCol);
|
||||
else $list.prepend($col);
|
||||
|
||||
const hasSub = !!(item.children && item.children.length);
|
||||
$col.attr('data-level', item.level)
|
||||
.attr('data-name', item.name)
|
||||
.attr('data-index', item.index)
|
||||
.toggleClass('has-children', hasSub)
|
||||
.toggleClass('no-child', !hasSub)
|
||||
.removeClass('is-expired');
|
||||
$col.find('.think-multiple-item-text').attr({'name': `${options.colName}[${item.name}]`, placeholder: `${options.colPlaceholder}${item.name}`});
|
||||
$col.find('.think-multiple-col-delete .btn').toggleClass('disabled', hasSub || this._items.length == 1);
|
||||
$col.find('.think-multiple-col-delete .btn.disabled').attr('title', options.deleteColTip);
|
||||
$col.find('.think-multiple-item-move').toggleClass('disabled', this._items.length == 1);
|
||||
return $col;
|
||||
}
|
||||
|
||||
render()
|
||||
{
|
||||
const items = this._items;
|
||||
let rootIndex = 0;
|
||||
items.forEach(item =>
|
||||
{
|
||||
updateItemName(item);
|
||||
if(item.level === 1)
|
||||
{
|
||||
item.index = rootIndex;
|
||||
rootIndex++;
|
||||
updateItemName(item, `${rootIndex}`);
|
||||
}
|
||||
if(item.children)
|
||||
{
|
||||
item.children.forEach((subItem, subIndex) =>
|
||||
{
|
||||
subItem.index = subIndex;
|
||||
updateItemName(subItem, `${item.name}.${subIndex + 1}`);
|
||||
});
|
||||
}
|
||||
});
|
||||
items.sort((a, b) => a.order - b.order);
|
||||
|
||||
const $list = this.$element.find('.think-multiple-body');
|
||||
const $cols = $list.find('.think-multiple-item').addClass('is-expired');
|
||||
let $preCol = null;
|
||||
items.forEach(item =>
|
||||
{
|
||||
$preCol = this._renderCol(item, $preCol, $list, $cols);
|
||||
});
|
||||
$cols.filter('.is-expired').remove();
|
||||
if(!this.$element.find('.think-multiple-buttons').length) this.$element.find('.think-multiple-body').append(`<div class="think-multiple-buttons"><button type="button" class="btn ghost text-primary rounded size-sm square btn-action action-add" data-action="sib"><i class="icon icon-plus"></i>${this.options.addColText}</button></div>`);
|
||||
this.$element.find('.think-multiple-buttons .action-add').prop('disabled', items?.length && items.length > 15).attr('title', items?.length && items.length > 15 ? this.options.addColTip : '');
|
||||
}
|
||||
|
||||
deleteItem(name)
|
||||
{
|
||||
const index = this._items.findIndex(x => x.name === name);
|
||||
if (index < 0) return;
|
||||
const item = this._items[index];
|
||||
this._items.splice(index, 1);
|
||||
const parent = this.getParent(item);
|
||||
if(parent)
|
||||
{
|
||||
const siblings = parent.children;
|
||||
const index = siblings.indexOf(item);
|
||||
if(index > -1) siblings.splice(index, 1);
|
||||
}
|
||||
if (!this._items.length) this.update(['1'], false);
|
||||
const inputValue = this.$element.find(`.think-multiple-item[data-id="${item.id}"] .think-multiple-item-text`).val();
|
||||
this.render();
|
||||
this.saveData(!!inputValue);
|
||||
}
|
||||
|
||||
focus(name)
|
||||
{
|
||||
const $step = this.$element.find('.think-multiple-item[data-name="' + name + '"] .think-multiple-item-text');
|
||||
if($step.length) $step[0].focus();
|
||||
}
|
||||
|
||||
addSib(focus = true)
|
||||
{
|
||||
if(this._items.length > 15) return;
|
||||
|
||||
const fromName = this.$element.find('.think-multiple-item').last().attr('data-name');
|
||||
const item = this.getByName(fromName);
|
||||
if(!item) return;
|
||||
|
||||
const newItem = this.updateItem(item.name);
|
||||
if(focus) this.focus(newItem.name);
|
||||
this.$element.find(`.think-multiple-item[data-id="${newItem.id}"] .think-multiple-item-text`).val('');
|
||||
}
|
||||
|
||||
moveAfter(fromName, toName)
|
||||
{
|
||||
if(fromName === toName) return;
|
||||
const item = this.getByName(fromName);
|
||||
if(!item) return;
|
||||
item.name = toName;
|
||||
this.updateItem(item);
|
||||
}
|
||||
|
||||
moveBefore(fromName, toName)
|
||||
{
|
||||
if(fromName === toName) return;
|
||||
const item = this.getByName(fromName);
|
||||
const toItem = this.getByName(toName);
|
||||
if(!item || !toItem) return;
|
||||
item.name = toItem.level > 1 ? `${toItem.parentName}.${+toItem.selfName - 1}` : `${+toItem.selfName - 1}`;
|
||||
this.updateItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
/* Define $.fn.ThinkMatrixOptions() helper. */
|
||||
ThinkMatrixOptions.defineFn();
|
||||
|
||||
/* Extend ThinkMatrixOptions to zui object. */
|
||||
$.extend(zui, {ThinkMatrixOptions});
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The thinkMatrixOptions widget class file of zin module of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
|
||||
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
|
||||
* @author Zemei Wang<wangzemei@easycorp.ltd>
|
||||
* @package zin
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
namespace zin;
|
||||
|
||||
require_once dirname(__DIR__) . DS . 'input' . DS . 'v1.php';
|
||||
|
||||
class thinkMatrixOptions extends wg
|
||||
{
|
||||
protected static array $defineProps = array
|
||||
(
|
||||
'id?: string="$GID"', // 组件根元素的 ID。
|
||||
'colName?: string="colFields"', // 列数据的名称。
|
||||
'cols?: array', // 列数据的默认值。
|
||||
'deleteColTip?: string', // 禁用删除提示。
|
||||
'addColTip?: string', // 禁用添加提示。
|
||||
'addColText: string', // 添加列按钮文字。
|
||||
);
|
||||
|
||||
public static function getPageJS(): ?string
|
||||
{
|
||||
return file_get_contents(__DIR__ . DS . 'js' . DS . 'v1.js');
|
||||
}
|
||||
|
||||
public static function getPageCSS(): ?string
|
||||
{
|
||||
return file_get_contents(__DIR__ . DS . 'css' . DS . 'v1.css');
|
||||
}
|
||||
|
||||
protected function build()
|
||||
{
|
||||
global $lang, $app;
|
||||
$app->loadLang('thinkstep');
|
||||
$id = $this->prop('id') ? $this->prop('id') : $this->gid;
|
||||
$deleteColTip = $this->prop('deleteColTip', $lang->thinkstep->tips->deleteCol);
|
||||
$addColText = $this->prop('addColText', $lang->thinkstep->addCol);
|
||||
$addColTip = $this->prop('addColTip', $lang->thinkstep->tips->addCol);
|
||||
|
||||
return div
|
||||
(
|
||||
setID($id),
|
||||
setClass('think-multiple w-full'),
|
||||
div(setClass('think-multiple-body flex overflow-x-auto overflow-y-hidden')),
|
||||
zui::thinkMatrixOptions
|
||||
(
|
||||
set::_to("#$id"),
|
||||
set::deleteColTip($deleteColTip),
|
||||
set::colPlaceholder($lang->thinkstep->label->columnTitle),
|
||||
set::addColText($addColText),
|
||||
set::addColTip($addColTip),
|
||||
set($this->props->pick(array('colName', 'cols')))
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
.think-form-batch .form-batch-container::-webkit-scrollbar {height: 5px;}
|
||||
.think-form-batch .form-batch-container::-webkit-scrollbar-track {background: var(--color-gray-200);}
|
||||
.think-form-batch .form-batch-head .form-label {text-overflow: unset; white-space: unset; font-weight: 400; position: static; padding: 0;}
|
||||
.think-form-batch .form-batch-table>*>tr>* {padding-top: calc(var(--space)); padding-bottom: calc(var(--space));}
|
||||
.think-form-batch .form-batch-row-actions {padding-bottom: .5rem !important; padding-top: .5rem !important;}
|
||||
.think-form-batch .form-batch-head[data-name="ACTIONS"], .think-form-batch .form-batch-row-actions {position: sticky; right: 0; background: var(--color-canvas);}
|
||||
.think-form-batch .form-batch-head[data-name="ACTIONS"] .form-label {display: none;}
|
||||
.think-form-batch .form-batch-row-actions .btn {color: var(--color-primary-500);}
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* 处理输入框值变化。
|
||||
* Handling input box value changes.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
window.changeRows = function()
|
||||
{
|
||||
const value = $(this).val();
|
||||
const intValue = value < 1 ? 1 : parseInt(value, 10);
|
||||
$(this).val(intValue);
|
||||
}
|
||||
|
||||
window.renderRowData = function()
|
||||
{
|
||||
$('.think-form-batch .form-batch-row-actions .btn[data-type="add"]').prop({
|
||||
disabled: $('.think-form-batch .form-batch-row-actions').length >= canAddRowsOfMulticol,
|
||||
title: $('.think-form-batch .form-batch-row-actions').length >= canAddRowsOfMulticol ? addRowsTips.replace('%d', canAddRowsOfMulticol) : '',
|
||||
});
|
||||
}
|
||||
|
||||
$(document).on('click', '.think-form-batch .form-batch-row-actions .btn', renderRowData);
|
||||
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace zin;
|
||||
|
||||
requireWg('thinkQuestion');
|
||||
|
||||
class thinkMulticolumn extends thinkQuestion
|
||||
{
|
||||
protected static array $defineProps = array
|
||||
(
|
||||
'fields?: array', // 列标题
|
||||
'requiredCols?: array', // 必填列
|
||||
'supportAdd?: int=1', // 是否支持用户添加行
|
||||
'canAddRows: int', // 可添加行数
|
||||
);
|
||||
|
||||
public static function getPageJS(): string
|
||||
{
|
||||
return file_get_contents(__DIR__ . DS . 'js' . DS . 'v1.js');
|
||||
}
|
||||
|
||||
public static function getPageCSS(): ?string
|
||||
{
|
||||
$baseCss = file_get_contents(dirname(__FILE__, 2) . DS . 'thinkstepbase' . DS . 'css' . DS . 'v1.css');
|
||||
return file_get_contents(__DIR__ . DS . 'css' . DS . 'v1.css') . $baseCss;
|
||||
}
|
||||
|
||||
protected function buildFormBatchItem(string $label, int $index): wg
|
||||
{
|
||||
$step = $this->prop('step');
|
||||
$key = $index + 1;
|
||||
$requiredCols = isset($step->options->requiredCols) ? $step->options->requiredCols : array();
|
||||
return formBatchItem
|
||||
(
|
||||
set::label($label),
|
||||
set::name("result[col$key][]"),
|
||||
set::width('110px'),
|
||||
set::required(in_array($key, $requiredCols))
|
||||
);
|
||||
}
|
||||
|
||||
protected function buildDetail(): array
|
||||
{
|
||||
global $lang;
|
||||
$detailWg = parent::buildDetail();
|
||||
list($step, $fields, $canAddRows, $mode) = $this->prop(array('step', 'fields', 'canAddRows', 'mode'));
|
||||
if($mode != 'detail') return array();
|
||||
|
||||
if($step)
|
||||
{
|
||||
$fields = $step->options->fields;
|
||||
$canAddRows = $step->options->supportAdd == 1 ? $step->options->canAddRows : 0;
|
||||
}
|
||||
jsVar('canAddRowsOfMulticol', $canAddRows + 5);
|
||||
jsVar('addRowsTips', $lang->thinkrun->tips->add);
|
||||
|
||||
$batchItems = array();
|
||||
foreach($fields as $key => $field)
|
||||
{
|
||||
$batchItems[] = $this->buildFormBatchItem($field, $key);
|
||||
}
|
||||
$detailWg[] = formBatch
|
||||
(
|
||||
setClass('think-form-batch'),
|
||||
set::minRows(5),
|
||||
set::actions(array()),
|
||||
set::onRenderRow(jsRaw('renderRowData')),
|
||||
$batchItems
|
||||
);
|
||||
return $detailWg;
|
||||
}
|
||||
|
||||
protected function buildFormItem(): array
|
||||
{
|
||||
global $lang, $app;
|
||||
$app->loadLang('thinkstep');
|
||||
$formItems = parent::buildFormItem();
|
||||
|
||||
list($step, $questionType, $required, $fields, $supportAdd, $canAddRows, $requiredCols) = $this->prop(array('step', 'questionType', 'required', 'fields', 'supportAdd', 'canAddRows', 'requiredCols'));
|
||||
$requiredItems = $lang->thinkstep->requiredList;
|
||||
|
||||
$requiredOptions = array();
|
||||
if($step)
|
||||
{
|
||||
$required = isset($step->options->required) ? $step->options->required : 1;
|
||||
if(!empty($step->options->fields)) $step->options->fields = is_string($step->options->fields) ? explode(', ', $step->options->fields) : array_values((array)$step->options->fields);
|
||||
$fields = $step->options->fields;
|
||||
$requiredCols = $required && isset($step->options->requiredCols) ? $step->options->requiredCols : '';
|
||||
$supportAdd = $step->options->supportAdd;
|
||||
$canAddRows = $supportAdd && isset($step->options->canAddRows) ? $step->options->canAddRows : '';
|
||||
foreach($fields as $key => $field) $requiredOptions[] = array('value' => $key + 1, 'text' => $field);
|
||||
}
|
||||
|
||||
$formItems[] = array(
|
||||
formHidden('options[questionType]', $questionType),
|
||||
formGroup
|
||||
(
|
||||
set::label($lang->thinkstep->label->columnTitle),
|
||||
setStyle(array('padding-bottom' => 'calc(4 * var(--space))')),
|
||||
thinkMatrixOptions(set::colName('options[fields]'), set::cols($fields))
|
||||
),
|
||||
formRow
|
||||
(
|
||||
setClass('mb-3'),
|
||||
formGroup
|
||||
(
|
||||
set::width('1/2'),
|
||||
set::label($lang->thinkstep->label->required),
|
||||
radioList
|
||||
(
|
||||
set::name('options[required]'),
|
||||
set::inline(true),
|
||||
set::value($required),
|
||||
set::items($requiredItems),
|
||||
on::change()->toggleClass('.required-options', 'hidden', 'target.value == 0')
|
||||
)
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
set::width('1/2'),
|
||||
set::label($lang->thinkstep->label->requiredCol),
|
||||
set::required(true),
|
||||
setClass('required-options', $required ? '' : 'hidden'),
|
||||
picker
|
||||
(
|
||||
set::name('options[requiredCols][]'),
|
||||
set::items($requiredOptions),
|
||||
set::value($requiredCols),
|
||||
set::multiple(true)
|
||||
)
|
||||
)
|
||||
),
|
||||
formRow
|
||||
(
|
||||
setClass('mb-3'),
|
||||
formGroup
|
||||
(
|
||||
setClass('w-1/2'),
|
||||
set::label($lang->thinkstep->label->isSupportAdd),
|
||||
set::labelHint($lang->thinkstep->tips->supportAdd),
|
||||
radioList
|
||||
(
|
||||
set::name('options[supportAdd]'),
|
||||
set::inline(true),
|
||||
set::items($lang->thinkstep->requiredList),
|
||||
set::value(is_null($supportAdd) ? 1 : $supportAdd),
|
||||
on::change()->toggleClass('.can-add-rows', 'hidden', 'target.value == 0')
|
||||
)
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
setClass('w-1/2 can-add-rows', (is_null($supportAdd) || $supportAdd) ? '' : 'hidden'),
|
||||
set::label($lang->thinkstep->label->canAddRows),
|
||||
set::required(true),
|
||||
input
|
||||
(
|
||||
set::type('number'),
|
||||
set::name('options[canAddRows]'),
|
||||
set::value($canAddRows),
|
||||
set::placeholder($lang->thinkstep->placeholder->inputContent),
|
||||
set::min(1),
|
||||
on::input('changeRows')
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
return $formItems;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
.model-pestel .model-block::after {position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: #fff; font-size: 160px; font-weight: 600; pointer-events: none;}
|
||||
.model-pestel .model-block::after {position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: #fff; font-size: 7.5rem; font-weight: 600; pointer-events: none;}
|
||||
.model-pestel .block-0 .model-block::after {content: 'P';}
|
||||
.model-pestel .block-1 .model-block::after {content: 'E';}
|
||||
.model-pestel .block-2 .model-block::after {content: 'S';}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* 修改引用题目。
|
||||
* Change quote title.
|
||||
*
|
||||
* @param event
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
window.changeQuoteTitle = function(event)
|
||||
{
|
||||
let fields = []; // 所有的多列填空题的配置项
|
||||
let requiredFields = []; // 多列填空题必填的标题id
|
||||
let citationItems = []; // 选择列picker组件的下拉项
|
||||
let isMulticolumn = false;
|
||||
let multicolumnStep = false
|
||||
|
||||
const quoteQuestions = $('.options-quote-title').data().quoteQuestions;
|
||||
const quoteTitle = $('input[name="options[quoteTitle]').val();
|
||||
|
||||
$.each(quoteQuestions, function(index, item) {
|
||||
const options = JSON.parse(item.options);
|
||||
if(quoteTitle && item.id == quoteTitle && options.questionType === 'multicolumn')
|
||||
{
|
||||
multicolumnStep = true;
|
||||
requiredFields = options.requiredCols;
|
||||
fields = options.fields;
|
||||
}
|
||||
});
|
||||
|
||||
/* 渲染选择列的下拉项。Renders a drop-down item for a select column.*/
|
||||
$.each(requiredFields, function(index, key) {
|
||||
/* 必填列的值-1是多列填空fields数组的key。The value of -1 in the required column is the key for the multi column fill in the blank fields array. */
|
||||
const keyValue = parseInt(key) - 1;
|
||||
citationItems.push({key: keyValue, value:keyValue, text: fields[keyValue]});
|
||||
})
|
||||
|
||||
$('.quote-citation').toggleClass('gap-4', multicolumnStep);
|
||||
$('.select-column').toggleClass('hidden', !multicolumnStep);
|
||||
$('.citation').toggleClass('hidden', multicolumnStep);
|
||||
$('.multicolumn-citation').toggleClass('hidden', !multicolumnStep);
|
||||
if(multicolumnStep)
|
||||
{
|
||||
/* 引用题目为多列填空题的时候,重置选择列的下拉选项和默认值, 修改引用方式为:引用某行输入项。 */
|
||||
/* When the reference question is a multi-column fill-in-the-blank question, reset the drop-down options and default values of the selected column, and change the reference method to: reference an input item in a row. */
|
||||
$('.select-column .picker-box').zui('picker').render({items: citationItems});
|
||||
$('.select-column .picker-box').zui('picker').$.setValue(citationItems[0]);
|
||||
$('.multicolumn-citation input[name="options[citation]"]').prop('checked', true);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 引用题目类型为多选或者单选题是,清空选择列的下拉选项和默认值,修改默认引用方式为:引用题的选中项。 */
|
||||
/* If the reference question type is multiple choice or single choice, clear the drop-down options and default values in the selection column, and change the default reference method to: reference the selected item in the question. */
|
||||
$('.select-column .picker-box').zui('picker').render({items: []});
|
||||
$('.select-column .picker-box').zui('picker').$.setValue('');
|
||||
if(citation) $('.citation input[id="options[citation]' + citation + '"]').prop('checked', true);
|
||||
}
|
||||
}
|
||||
@@ -14,14 +14,25 @@ class thinkRadio extends thinkQuestion
|
||||
(
|
||||
'enableOther?: bool',
|
||||
'fields?: array',
|
||||
'setOption?: bool=false',
|
||||
'quoteTitle?: string',
|
||||
'quoteQuestions?: array',
|
||||
'citation?: int=1',
|
||||
'selectColumn?: int',
|
||||
);
|
||||
|
||||
public static function getPageJS(): ?string
|
||||
{
|
||||
return file_get_contents(__DIR__ . DS . 'js' . DS . 'v1.js');
|
||||
}
|
||||
|
||||
protected function buildDetail(): array
|
||||
{
|
||||
global $lang;
|
||||
$detailWg = parent::buildDetail();
|
||||
list($step, $mode) = $this->prop(array('step', 'mode'));
|
||||
list($step, $mode, $isRun, $quoteQuestions) = $this->prop(array('step', 'mode', 'isRun', 'quoteQuestions'));
|
||||
if($mode != 'detail') return array();
|
||||
jsVar('citation', !empty($step->options->citation) ? $step->options->citation : 1);
|
||||
|
||||
$answer = $step->answer;
|
||||
$result = isset($answer->result) ? $answer->result : array();
|
||||
@@ -29,7 +40,9 @@ class thinkRadio extends thinkQuestion
|
||||
|
||||
$fields = $step->options->fields ?? array();
|
||||
$items = array();
|
||||
foreach($fields as $field) $items[] = array('text' => $field, 'value' => $field);
|
||||
|
||||
if(!empty($fields)) foreach($fields as $field) $items[] = array('text' => $field, 'value' => $field);
|
||||
if(empty($fields) && !$isRun) $items[] = array('text' => $lang->thinkstep->optionReference, 'disabledPrefix' => true);
|
||||
if(!empty($step->options->enableOther)) $items[] = array('text' => $lang->other, 'value' => 'other', 'isOther' => '1', 'other' => isset($answer->other) ? $answer->other : '');
|
||||
|
||||
$detailWg[] = thinkBaseCheckbox
|
||||
@@ -38,7 +51,24 @@ class thinkRadio extends thinkQuestion
|
||||
set::items($items),
|
||||
set::name('result[]'),
|
||||
set::value($step->options->questionType == 'radio' ? ($result[0] ?? '') : $result),
|
||||
set::disabled($step->options->questionType == 'checkbox' && !empty($step->options->setOption) && $step->options->setOption == 1 && !$isRun)
|
||||
);
|
||||
$quoteItem = $step->options->questionType == 'checkbox' && !empty($step->options->setOption) && $step->options->setOption == 1;
|
||||
$quoteName = '';
|
||||
|
||||
if($quoteItem && !empty($quoteQuestions))
|
||||
{
|
||||
foreach($quoteQuestions as $item)
|
||||
{
|
||||
if(!$isRun && $item->id == $step->options->quoteTitle) $quoteName = $item->index . '. ' . $item->title;
|
||||
if($isRun && $item->origin == $step->options->quoteTitle) $quoteName = $item->title;
|
||||
}
|
||||
$detailWg[] = div
|
||||
(
|
||||
setClass('mt-3'),
|
||||
sprintf($lang->thinkstep->tips->checkbox, $quoteName)
|
||||
);
|
||||
}
|
||||
return $detailWg;
|
||||
}
|
||||
|
||||
@@ -46,22 +76,148 @@ class thinkRadio extends thinkQuestion
|
||||
{
|
||||
global $lang, $app;
|
||||
$app->loadLang('thinkstep');
|
||||
$formItems = parent::buildFormItem();
|
||||
|
||||
list($step, $questionType, $required, $enableOther, $fields) = $this->prop(array('step', 'questionType', 'required', 'enableOther', 'fields'));
|
||||
$formItems = parent::buildFormItem();
|
||||
list($step, $questionType, $required, $enableOther, $fields, $setOption, $quoteTitle, $quoteQuestions, $citation, $selectColumn) = $this->prop(array('step', 'questionType', 'required', 'enableOther', 'fields', 'setOption', 'quoteTitle', 'quoteQuestions', 'citation', 'selectColumn'));
|
||||
|
||||
$requiredItems = $lang->thinkstep->requiredList;
|
||||
if($step)
|
||||
{
|
||||
$enableOther = $step->options->enableOther ?? 0;
|
||||
$required = $step->options->required;
|
||||
$enableOther = $step->options->enableOther ?? 0;
|
||||
$required = $step->options->required;
|
||||
$setOption = isset($step->options->setOption) ? $step->options->setOption : false;
|
||||
$defaultQuote = !empty($quoteQuestions) ? $quoteQuestions[0]->id : null;
|
||||
$quoteTitle = isset($step->options->quoteTitle) ? $step->options->quoteTitle : $defaultQuote;
|
||||
$citation = isset($step->options->citation) ? $step->options->citation : 1;
|
||||
$selectColumn = isset($step->options->selectColumn) ? $step->options->selectColumn : null;
|
||||
if(!empty($step->options->fields)) $step->options->fields = is_string($step->options->fields) ? explode(', ', $step->options->fields) : array_values((array)$step->options->fields);
|
||||
$fields = $step->options->fields ?? array();
|
||||
$fields = !empty($step->options->fields) ? $step->options->fields : array('', '', '');
|
||||
}
|
||||
|
||||
$quoteQuestionsItems = array();
|
||||
if(!empty($quoteQuestions))
|
||||
{
|
||||
foreach($quoteQuestions as $item)
|
||||
{
|
||||
$quoteQuestionsItems[] = array('text' => $item->title, 'value' => $item->id);
|
||||
}
|
||||
}
|
||||
|
||||
$formItems[] = array(
|
||||
formHidden('options[questionType]', $questionType),
|
||||
$questionType == 'checkbox' ? array
|
||||
(
|
||||
formRow
|
||||
(
|
||||
formGroup
|
||||
(
|
||||
setClass('w-66'),
|
||||
set::label($lang->thinkstep->label->setOption),
|
||||
radioList
|
||||
(
|
||||
set::name('options[setOption]'),
|
||||
set::inline(true),
|
||||
set::value($setOption),
|
||||
set::items($lang->thinkstep->setOptionList),
|
||||
on::change()
|
||||
->const('maxCountPlaceholder', $lang->thinkstep->placeholder->maxCount)
|
||||
->const('inputContent', $lang->thinkstep->placeholder->inputContent)
|
||||
->do("
|
||||
$('.think-options-field').toggleClass('hidden', target.value == 1);
|
||||
$('.think-quote').toggleClass('hidden', target.value == 0);
|
||||
if(target.value == 1)
|
||||
{
|
||||
$('.min-count input').val(1).attr('disabled', 'disabled');
|
||||
$('.max-count input').val('').attr('placeholder', maxCountPlaceholder).attr('disabled', 'disabled');
|
||||
}
|
||||
else
|
||||
{
|
||||
$('.min-count input').val('').removeAttr('disabled');
|
||||
$('.max-count input').attr('placeholder', inputContent).removeAttr('disabled');
|
||||
}
|
||||
$('.text-danger').remove();
|
||||
$('.has-error').removeClass('has-error');
|
||||
")
|
||||
)
|
||||
),
|
||||
icon
|
||||
(
|
||||
setClass('mt-9 text-gray-400 cursor-pointer ml-1 text-base pt-0.5'),
|
||||
toggle::tooltip(array('placement' => 'top', 'title' => $lang->thinkstep->tips->setOption, 'width' => '220px', 'className' => 'bg-white text-gray border border-gray-300')),
|
||||
'help'
|
||||
)
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
setClass('think-quote', $setOption == 0 ? 'hidden' : ''),
|
||||
set::label($lang->thinkstep->label->quoteTitle),
|
||||
set::labelClass('required'),
|
||||
picker
|
||||
(
|
||||
setdata('quote-questions', $quoteQuestions),
|
||||
set(array
|
||||
(
|
||||
'class' => 'options-quote-title',
|
||||
'name' => 'options[quoteTitle]',
|
||||
'placeholder' => $lang->thinkstep->placeholder->quoteTitle,
|
||||
'items' => $quoteQuestionsItems,
|
||||
'value' => !empty($quoteTitle) && !empty($quoteQuestionsItems) ? $quoteTitle : '',
|
||||
'disabled' => empty($quoteQuestions),
|
||||
'title' => empty($quoteQuestions) ? $lang->thinkstep->tips->quoteTitle : null,
|
||||
'required' => true,
|
||||
)),
|
||||
bind::change('changeQuoteTitle(event)')
|
||||
)
|
||||
),
|
||||
formRow
|
||||
(
|
||||
setClass('think-quote quote-citation', $setOption == 0 ? 'hidden' : ''),
|
||||
formGroup
|
||||
(
|
||||
setClass('citation'),
|
||||
set::label($lang->thinkstep->label->citation),
|
||||
set::labelClass('required'),
|
||||
radioList
|
||||
(
|
||||
set::name('options[citation]'),
|
||||
set::inline(true),
|
||||
set::value($citation),
|
||||
set::items($lang->thinkstep->citationList)
|
||||
)
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
setClass('multicolumn-citation w-1/2', $citation != 3 ? 'hidden' : ''),
|
||||
set::label($lang->thinkstep->label->citation),
|
||||
set::labelClass('required'),
|
||||
radioList
|
||||
(
|
||||
set::name('options[citation]'),
|
||||
set::inline(true),
|
||||
set::value($citation),
|
||||
set::items($lang->thinkstep->multiCitationList)
|
||||
)
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
setClass('select-column', $citation != 3 ? 'hidden' : ''),
|
||||
set::label($lang->thinkstep->label->selectColumn),
|
||||
set::labelClass('required'),
|
||||
picker(
|
||||
set(array(
|
||||
'name' => 'options[selectColumn]',
|
||||
'placeholder' => $lang->thinkstep->placeholder->quoteTitle,
|
||||
'required' => true,
|
||||
'items' => array(),
|
||||
'value' => $selectColumn,
|
||||
))
|
||||
)
|
||||
)
|
||||
)
|
||||
) : null,
|
||||
formGroup
|
||||
(
|
||||
setClass('think-options-field', ($questionType === 'checkbox' && $setOption == 1) ? 'hidden' : ''),
|
||||
set::label($lang->thinkstep->label->option),
|
||||
thinkOptions
|
||||
(
|
||||
|
||||
@@ -26,7 +26,7 @@ class thinkResult extends wg
|
||||
list($wizard, $mode, $blocks) = $this->prop(array('wizard', 'mode', 'blocks'));
|
||||
|
||||
$model = $wizard->model;
|
||||
$wgMap = array('swot' => 'thinkSwot', 'pffa' => 'thinkPffa', 'pest' => 'thinkPestel', '4p' => 'think4p');
|
||||
$wgMap = array('swot' => 'thinkSwot', 'pffa' => 'thinkPffa', 'pest' => 'thinkPestel', 'pestel' => 'thinkPestel', '4p' => 'think4p', '4p2' => 'think4p');
|
||||
if(!isset($wgMap[$model])) return array();
|
||||
|
||||
return createWg($wgMap[$model], array(set::mode($mode), set::blocks($blocks)));
|
||||
|
||||
@@ -9,11 +9,12 @@ class thinkStep extends wg
|
||||
'action?: string="detail"',
|
||||
'addType?: string',
|
||||
'isRun?: bool=false', // 是否是分析活动
|
||||
'quoteQuestions?: array', // 引用题目的下拉选项
|
||||
);
|
||||
|
||||
protected function buildBody(): wg|array
|
||||
{
|
||||
list($item, $action, $addType, $isRun) = $this->prop(array('item', 'action', 'addType', 'isRun'));
|
||||
list($item, $action, $addType, $isRun, $quoteQuestions) = $this->prop(array('item', 'action', 'addType', 'isRun', 'quoteQuestions'));
|
||||
|
||||
$step = $addType ? null : $item;
|
||||
$questionType = $addType ? $addType : ($item->options->questionType ?? '');
|
||||
@@ -21,8 +22,9 @@ class thinkStep extends wg
|
||||
if($addType === 'transition' || !$addType && $item->type === 'transition') return thinkTransition(set::step($step), set::mode($action), set::isRun($isRun));
|
||||
if($questionType === 'input') return thinkInput(set::step($step), set::questionType('input'), set::mode($action), set::isRun($isRun));
|
||||
if($questionType === 'radio') return thinkRadio(set::step($step), set::questionType('radio'), set::mode($action), set::isRun($isRun));
|
||||
if($questionType === 'checkbox') return thinkCheckbox(set::step($step), set::questionType('checkbox'), set::mode($action), set::isRun($isRun));
|
||||
if($questionType === 'checkbox') return thinkCheckbox(set::step($step), set::questionType('checkbox'), set::mode($action), set::isRun($isRun), set::quoteQuestions($quoteQuestions));
|
||||
if($questionType === 'tableInput') return thinkTableInput(set::step($step), set::questionType('tableInput'), set::mode($action), set::isRun($isRun));
|
||||
if($questionType === 'multicolumn') return thinkMulticolumn(set::step($step), set::questionType('multicolumn'), set::mode($action), set::isRun($isRun));
|
||||
return array();
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ class thinkStepBase extends wg
|
||||
{
|
||||
$questionType = $options->questionType;
|
||||
$tips = $lang->thinkstep->$questionType;
|
||||
if($options->required && $questionType == 'checkbox')
|
||||
if($options->required && $questionType == 'checkbox' && $options->setOption == 0)
|
||||
{
|
||||
$tips = $lang->thinkrun->requiredTitle[$questionType];
|
||||
$tips = str_replace(array('%min%', '%max%'), array($options->minCount, $options->maxCount), $tips);
|
||||
@@ -75,6 +75,7 @@ class thinkStepBase extends wg
|
||||
(
|
||||
setClass('h-full text-fore text-lg'),
|
||||
$requiredSymbal,
|
||||
isset($step->index) ? $step->index . '. ' : '',
|
||||
$step->title,
|
||||
$questionTips,
|
||||
$errorText
|
||||
|
||||
@@ -53,7 +53,7 @@ class thinkStepMenu extends wg
|
||||
$unClickable = $toggleNonNodeShow && $setting->id != $activeKey && $setting->type != 'node' && json_decode($setting->answer) == null;
|
||||
$item = array(
|
||||
'key' => $setting->id,
|
||||
'text' => $setting->title,
|
||||
'text' => (isset($setting->index) ? ($setting->index . '. ') : '') . $setting->title,
|
||||
'hint' => $unClickable ? $this->lang->thinkrun->error->unanswered :$setting->title,
|
||||
'url' => $unClickable || !$canView ? '' : $setting->url,
|
||||
'data-id' => $setting->id,
|
||||
@@ -188,34 +188,25 @@ class thinkStepMenu extends wg
|
||||
|
||||
if($canCreate && (($showQuestionOfNode && $item->type == 'node') || $item->hasSameQuestion || $item->type == 'question')) $menus = array_merge($menus, array(
|
||||
array('type' => 'divider'),
|
||||
array(
|
||||
'key' => 'radio',
|
||||
'icon' => 'radio',
|
||||
'text' => $this->lang->thinkstep->createStep . $this->lang->thinkstep->actions['radio'],
|
||||
'onClick' => jsRaw("() => addQuestion({$item->id}, {$parentID}, 'question', 'radio')"),
|
||||
),
|
||||
array(
|
||||
'key' => 'checkbox',
|
||||
'icon' => 'checkbox',
|
||||
'text' => $this->lang->thinkstep->createStep . $this->lang->thinkstep->actions['checkbox'],
|
||||
'onClick' => jsRaw("() => addQuestion({$item->id}, {$parentID}, 'question', 'checkbox')"),
|
||||
),
|
||||
array(
|
||||
'key' => 'input',
|
||||
'icon' => 'input',
|
||||
'text' => $this->lang->thinkstep->createStep . $this->lang->thinkstep->actions['input'],
|
||||
'onClick' => jsRaw("() => addQuestion({$item->id}, {$parentID}, 'question', 'input')"),
|
||||
),
|
||||
array(
|
||||
'key' => 'tableInput',
|
||||
'icon' => 'cell-input',
|
||||
'text' => $this->lang->thinkstep->createStep . $this->lang->thinkstep->actions['tableInput'],
|
||||
'onClick' => jsRaw("() => addQuestion({$item->id}, {$parentID}, 'question', 'tableInput')"),
|
||||
),
|
||||
$this->buildMenuItem('radio', 'radio', $this->lang->thinkstep->createStep . $this->lang->thinkstep->actions['radio'], $item, $parentID, 'radio'),
|
||||
$this->buildMenuItem('checkbox', 'checkbox', $this->lang->thinkstep->createStep . $this->lang->thinkstep->actions['checkbox'], $item, $parentID, 'checkbox'),
|
||||
$this->buildMenuItem('input', 'input', $this->lang->thinkstep->createStep . $this->lang->thinkstep->actions['input'], $item, $parentID, 'input'),
|
||||
$this->buildMenuItem('tableInput', 'cell-input', $this->lang->thinkstep->createStep . $this->lang->thinkstep->actions['tableInput'], $item, $parentID, 'tableInput'),
|
||||
$this->buildMenuItem('multicolumn', 'multi-input', $this->lang->thinkstep->createStep . $this->lang->thinkstep->actions['multicolumn'], $item, $parentID, 'multicolumn'),
|
||||
));
|
||||
return $menus;
|
||||
}
|
||||
|
||||
private function buildMenuItem(string $key, string $icon, string $text, object $item, int $parentID): array
|
||||
{
|
||||
return array(
|
||||
'key' => $key,
|
||||
'icon' => $icon,
|
||||
'text' => $text,
|
||||
'onClick' => jsRaw("() => addQuestion({$item->id}, {$parentID}, 'question', '{$key}')"),
|
||||
);
|
||||
}
|
||||
|
||||
private function buildActions(): node
|
||||
{
|
||||
return btn
|
||||
@@ -238,7 +229,7 @@ class thinkStepMenu extends wg
|
||||
(
|
||||
div
|
||||
(
|
||||
setClass('think-node-menu rounded bg-white col bg-canvas pb-3 h-full'),
|
||||
setClass('think-node-menu rounded bg-white col bg-canvas pb-3 h-full no-morph'),
|
||||
zui::$treeType
|
||||
(
|
||||
set::_id('thinkNodeMenu'),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.model-swot .block-0, .model-swot .block-1 {border-bottom: none;}
|
||||
.model-swot .model-block:nth-child(odd) {border-right: none;}
|
||||
.model-swot .model-block::after {position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: #fff; font-size: 160px; font-weight: 600; pointer-events: none;}
|
||||
.model-swot .model-block::after {position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: #fff; font-size: 9rem; font-weight: 600; pointer-events: none;}
|
||||
.model-swot .model-block.block-0::after {content: 'S';}
|
||||
.model-swot .model-block.block-1::after {content: 'W';}
|
||||
.model-swot .model-block.block-2::after {content: 'O';}
|
||||
|
||||
@@ -45,12 +45,12 @@ class thumbnail extends wg
|
||||
(
|
||||
div
|
||||
(
|
||||
setClass('flex items-center justify-center cursor-pointer bg-gray-100 w-full h-60'),
|
||||
setClass('flex items-center justify-center cursor-pointer bg-white w-full h-60 '),
|
||||
setData(array('on' => 'click', 'call' => 'uploadThumbnail')),
|
||||
img
|
||||
(
|
||||
setID('thumbnail-img'),
|
||||
setClass('w-full h-full' . ($src ? '' : ' hidden')),
|
||||
setClass('h-full' . ($src ? '' : ' hidden')),
|
||||
set::src($src),
|
||||
set::title($tips)
|
||||
),
|
||||
|
||||
@@ -90,8 +90,8 @@ class treeEditor extends wg
|
||||
$item['actions'] = array();
|
||||
$item['actions']['items'] = array();
|
||||
|
||||
if($canEdit) $item['actions']['items'][] = array('key' => 'edit', 'icon' => 'edit', 'data-toggle' => 'modal', 'url' => createLink('tree', 'edit', 'moduleID=' . $item['id'] . '&type=' . ($viewType ? $viewType : $item['type'])));
|
||||
if($itemCanSplit) $item['actions']['items'][] = array('key' => 'view', 'icon' => 'split', 'url' => $item['url'], 'data-app' => $app->tab);
|
||||
if($canEdit && $editType != 'task') $item['actions']['items'][] = array('key' => 'edit', 'icon' => 'edit', 'data-toggle' => 'modal', 'url' => createLink('tree', 'edit', 'moduleID=' . $item['id'] . '&type=' . ($viewType ? $viewType : $item['type'])));
|
||||
if($itemCanSplit) $item['actions']['items'][] = array('key' => 'view', 'icon' => 'split', 'url' => $item['url'], 'data-app' => $app->tab);
|
||||
}
|
||||
elseif(isset($item['type']) && $item['type'] != 'branch')
|
||||
{
|
||||
|
||||
@@ -107,7 +107,7 @@ function loadPlanForTwins(productID, branch, branchIndex)
|
||||
{
|
||||
/* Load plan */
|
||||
if(branch == '0') branch = '';
|
||||
planLink = $.createLink('product', 'ajaxGetPlans', 'productID=' + productID + '&branch=' + branch + '&planID=0&fieldID=' + branchIndex + '&needCreate=false&expi red=unexpired¶m=skipParent,forStory,' + config.currentMethod);
|
||||
planLink = $.createLink('product', 'ajaxGetPlans', 'productID=' + productID + '&branch=' + branch + '¶ms=unexpired,noclosed&skipParent=true');
|
||||
if(branchIndex > 0)
|
||||
{
|
||||
var $planIdBox = $('.addBranchesBox'+ branchIndex +' #planIdBox');
|
||||
|
||||
Vendored
+2
-29
@@ -224,38 +224,11 @@ pipeline {
|
||||
// 仅制作开源版时不上传收费版相关源码
|
||||
if (params.publish_exts!="open") {
|
||||
buildPkg.uploadSource("php8.1", ["php7.0", "php7.1", "php7.2_7.4"])
|
||||
env.PLUGIN_SAFE_EXIST = sh(returnStatus: true, script: 'test -d $SRC_ZENTAOEXT_PATH/zentaopro/safe')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage("SAFe") {
|
||||
when {
|
||||
allOf {
|
||||
environment name: 'PLUGIN_SAFE_EXIST', value: '0'
|
||||
}
|
||||
}
|
||||
steps {
|
||||
container('package') {
|
||||
sh 'cd $SRC_ZENTAOEXT_PATH && make safe'
|
||||
sh 'cp ${ZENTAO_BUILD_PATH}/safe.zip ${ZENTAO_BUILD_PATH}/safe-php8.1.zip'
|
||||
sh 'cp ${ZENTAO_BUILD_PATH}/safe.zip ${ZENTAO_RELEASE_PATH}/safe-php8.1.zip'
|
||||
sh '''${ZENTAO_BUILD_PATH}/script/downgrade/dirDowngrade.sh -n safe -s -i \
|
||||
-p 7.2,7.1,7.0 \
|
||||
-c ${PWD}/misc -r $SRC_ZENTAOEXT_PATH/zentaopro -o ${ZENTAO_BUILD_PATH} \
|
||||
safe '''
|
||||
sh 'cp -v ${ZENTAO_BUILD_PATH}/safe-*.zip ${ZENTAO_RELEASE_PATH}/'
|
||||
sh 'cd $SRC_ZENTAOEXT_PATH && make encrypt-safe-all'
|
||||
}
|
||||
script {
|
||||
env.UPLOADED_PLUGIN_SAFE = 'true'
|
||||
buildPkg.uploadSAFe(["php7.0", "php7.1", "php7.2_7.4", "php8.1"])
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // End Build
|
||||
}
|
||||
@@ -589,7 +562,7 @@ pipeline {
|
||||
sh 'make test-biz-k8s'
|
||||
sh 'make build-biz-k8s'
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (env.PUBLISH_MAX=="true") {
|
||||
sh 'make test-max'
|
||||
@@ -598,7 +571,7 @@ pipeline {
|
||||
sh 'make test-max-k8s'
|
||||
sh 'make build-max-k8s'
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (env.PUBLISH_IPD=="true") {
|
||||
sh 'make test-ipd'
|
||||
|
||||
@@ -248,6 +248,7 @@ class admin extends control
|
||||
public function setModule()
|
||||
{
|
||||
$this->loadModel('setting');
|
||||
$this->loadModel('project');
|
||||
|
||||
if($_POST)
|
||||
{
|
||||
@@ -266,6 +267,14 @@ class admin extends control
|
||||
$URAndSR = $this->config->edition == 'ipd' ? 1 : zget($data->module, 'productUR', 0);
|
||||
if($enableER && !$URAndSR) $this->send(array('result' => 'fail', 'message' => $this->lang->admin->notice->openUR));
|
||||
|
||||
foreach($closedFeatures as $closedFeature)
|
||||
{
|
||||
if(in_array($closedFeature, array('productER', 'productUR')))
|
||||
{
|
||||
$this->project->unlinkStoryByType(0, $closedFeature == 'productER' ? 'epic' : 'requirement');
|
||||
}
|
||||
}
|
||||
|
||||
$this->setting->setItem('system.common.closedFeatures', implode(',', $closedFeatures));
|
||||
$this->setting->setItem('system.common.global.scoreStatus', zget($data->module, 'myScore', 0));
|
||||
$this->setting->setItem('system.custom.enableER', $enableER);
|
||||
|
||||
@@ -7,6 +7,26 @@ function changeModule($target)
|
||||
}
|
||||
else
|
||||
{
|
||||
if(name == 'module[productUR]' || name == 'module[productER]')
|
||||
{
|
||||
zui.Modal.confirm(
|
||||
{
|
||||
message: confirmDisableStoryType,
|
||||
icon: 'icon-exclamation-sign',
|
||||
iconClass: 'warning-pale rounded-full icon-2x'
|
||||
}).then((res) =>
|
||||
{
|
||||
if(res)
|
||||
{
|
||||
$("input[type=hidden][name='" + name + "']").val('0').removeAttr('disabled');
|
||||
return false;
|
||||
}
|
||||
|
||||
$target.prop('checked', true);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
$("input[type=hidden][name='" + name + "']").val('0').removeAttr('disabled');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -188,7 +188,7 @@ class adminModel extends model
|
||||
|
||||
if($menuKey == 'company' && $this->app->rawModule != 'convert')
|
||||
{
|
||||
$dept = $this->dao->select('id')->from(TABLE_DEPT)->fetch();
|
||||
$dept = $this->dao->select('id')->from(TABLE_DEPT)->limit(1)->fetch();
|
||||
if($dept && common::hasPriv('company', 'browse')) $menu['link'] = helper::createLink('company', 'browse');
|
||||
}
|
||||
|
||||
@@ -428,11 +428,9 @@ class adminModel extends model
|
||||
*/
|
||||
public function genDateUsed(): object
|
||||
{
|
||||
$firstUseDate = $this->dao->select('date')->from(TABLE_ACTION)
|
||||
->where('date')->gt('1970-01-01')
|
||||
$firstUseDate = $this->dao->select('min(`date`) as `date`')->from(TABLE_ACTION)
|
||||
->where('`date`')->gt('1970-01-01')
|
||||
->andWhere('actor')->eq($this->app->user->account)
|
||||
->orderBy('date_asc')
|
||||
->limit('1')
|
||||
->fetch('date');
|
||||
|
||||
if($firstUseDate) $firstUseDate = substr($firstUseDate, 0, 10);
|
||||
|
||||
@@ -10,6 +10,10 @@ declare(strict_types=1);
|
||||
*/
|
||||
namespace zin;
|
||||
|
||||
global $app;
|
||||
$app->loadLang('project');
|
||||
jsVar('confirmDisableStoryType', $lang->project->confirmDisableStoryType);
|
||||
|
||||
if(strpos(",$disabledFeatures,", ",productUR,") !== false) $disabledFeatures .= ',productER';
|
||||
|
||||
$rows = array();
|
||||
|
||||
@@ -27,9 +27,9 @@ window.testConnection = () =>
|
||||
});
|
||||
};
|
||||
|
||||
const handleModelTypeChange = () =>
|
||||
const handleModelTypeChange = (modelType = '') =>
|
||||
{
|
||||
const modelType = $('select[name="type"]').val();
|
||||
if(!modelType) modelType = $('select[name="type"]').val();
|
||||
const vendorList = window.vendorListLang[modelType];
|
||||
$('select[name="vendor"]').html('');
|
||||
for(const vendor in vendorList) $('select[name="vendor"]').append('<option value="' + vendor + '">' + vendorList[vendor] + '</option>');
|
||||
@@ -37,9 +37,9 @@ const handleModelTypeChange = () =>
|
||||
$('select[name="vendor"]').trigger('change');
|
||||
};
|
||||
|
||||
const handleVendorChange = () =>
|
||||
const handleVendorChange = (vendor = '') =>
|
||||
{
|
||||
const vendor = $('select[name="vendor"]').val();
|
||||
vendor ? $('select[name="vendor"]').val(vendor) : (vendor = $('select[name="vendor"]').val());
|
||||
const requiredFields = window.vendorList[vendor]['credentials'];
|
||||
const vendorTip = window.vendorTipsLang[vendor];
|
||||
$('.vendor-tips').html(vendorTip ? vendorTip : '');
|
||||
@@ -53,8 +53,8 @@ const handleProxyTypeChange = () =>
|
||||
|
||||
$(() =>
|
||||
{
|
||||
handleModelTypeChange();
|
||||
handleVendorChange();
|
||||
handleModelTypeChange(window.modelData.type);
|
||||
handleVendorChange(window.modelData.vendor);
|
||||
handleProxyTypeChange();
|
||||
$('select[name="type"]').on('change', handleModelTypeChange);
|
||||
$('select[name="vendor"]').on('change', handleVendorChange);
|
||||
|
||||
@@ -10,6 +10,7 @@ $allCredFields = array_unique(array_reduce((array)$config->ai->vendorList,
|
||||
jsVar('window.vendorList', $config->ai->vendorList);
|
||||
jsVar('window.vendorListLang', $lang->ai->models->vendorList);
|
||||
jsVar('window.vendorTipsLang', $lang->ai->models->vendorTips);
|
||||
jsVar('window.modelData', $model);
|
||||
|
||||
formPanel
|
||||
(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The model file of api module of ZenTaoCMS.
|
||||
* The model file of api module of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
|
||||
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The model file of backup module of ZenTaoCMS.
|
||||
* The model file of backup module of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
|
||||
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
$(function()
|
||||
{
|
||||
if(ganttPlans) initWaterfallGanttBlock();
|
||||
if(ganttPlans)
|
||||
{
|
||||
$('#ganttPlans .gantt-plan, #ganttContainer .gantt-row').remove();
|
||||
initWaterfallGanttBlock();
|
||||
}
|
||||
$('#ganttContainer').off('scroll').on('scroll', function(e) {
|
||||
$('#ganttPlans').scrollTop = e.target.scrollTop;
|
||||
$('#ganttPlans')[0].scrollTop = e.target.scrollTop;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -439,7 +439,7 @@ class blockZen extends block
|
||||
$yesterday = strtotime("-1 day");
|
||||
/* 获取昨日完成的任务数。 */
|
||||
$finishTask = 0;
|
||||
$finishTaskGroup = $this->loadModel('metric')->getResultByCode('count_of_daily_finished_task_in_user', array('user' => $this->app->user->account, 'year' => date('Y', $yesterday), 'month' => date('m', $yesterday), 'day' => date('d', $yesterday)), 'realtime', null, $this->config->vision);
|
||||
$finishTaskGroup = $this->loadModel('metric')->getResultByCode('count_of_daily_finished_task_in_user', array('user' => $this->app->user->account, 'year' => date('Y', $yesterday), 'month' => date('m', $yesterday), 'day' => date('d', $yesterday)), 'cron', null, $this->config->vision);
|
||||
if(!empty($finishTaskGroup))
|
||||
{
|
||||
$finishTaskGroup = reset($finishTaskGroup);
|
||||
@@ -448,7 +448,7 @@ class blockZen extends block
|
||||
|
||||
/* 获取昨日解决的Bug数。 */
|
||||
$fixBug = 0;
|
||||
$fixBugGroup = $this->metric->getResultByCode('count_of_daily_fixed_bug_in_user', array('user' => $this->app->user->account, 'year' => date('Y', $yesterday), 'month' => date('m', $yesterday), 'day' => date('d', $yesterday)));
|
||||
$fixBugGroup = $this->metric->getResultByCode('count_of_daily_fixed_bug_in_user', array('user' => $this->app->user->account, 'year' => date('Y', $yesterday), 'month' => date('m', $yesterday), 'day' => date('d', $yesterday)), 'cron', null, $this->config->vision);
|
||||
if(!empty($fixBugGroup))
|
||||
{
|
||||
$fixBugGroup = reset($fixBugGroup);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The model file of branch module of ZenTaoCMS.
|
||||
* The model file of branch module of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
|
||||
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
|
||||
@@ -262,7 +262,7 @@ class branchModel extends model
|
||||
$this->dao->update(TABLE_BRANCH)->data($branch)
|
||||
->where('id')->eq($branchID)
|
||||
->batchCheck($this->config->branch->edit->requiredFields, 'notempty')
|
||||
->checkIF(!empty($branch->name)&& $branch->name != $oldBranch->name, 'name', 'unique', "product = $oldBranch->product")
|
||||
->checkIF(!empty($branch->name) && $branch->name != $oldBranch->name, 'name', 'unique', "product = $oldBranch->product && id != $oldBranch->id")
|
||||
->exec();
|
||||
if(dao::isError()) return false;
|
||||
|
||||
|
||||
@@ -269,7 +269,7 @@ class bug extends control
|
||||
$oldBug = $this->bug->getByID($bugID);
|
||||
if(!empty($_POST))
|
||||
{
|
||||
$formData = form::data($this->config->bug->form->edit);
|
||||
$formData = form::data($this->config->bug->form->edit, $bugID);
|
||||
$bug = $this->bugZen->prepareEditExtras($formData, $oldBug);
|
||||
$this->bugZen->checkRquiredForEdit($bug);
|
||||
|
||||
@@ -315,7 +315,7 @@ class bug extends control
|
||||
{
|
||||
/* 初始化 bug 数据。*/
|
||||
/* Init bug data. */
|
||||
$bug = form::data($this->config->bug->form->assignTo)->add('id', $bugID)->get();
|
||||
$bug = form::data($this->config->bug->form->assignTo, $bugID)->add('id', $bugID)->get();
|
||||
|
||||
if($this->app->rawMethod == 'batchassignto') unset($bug->mailto);
|
||||
|
||||
@@ -376,7 +376,7 @@ class bug extends control
|
||||
|
||||
/* 构造 bug 的表单数据。*/
|
||||
/* Structure the bug form data. */
|
||||
$bug = form::data($this->config->bug->form->confirm)->add('id', $bugID)->setDefault('confirmed', 1)->get();
|
||||
$bug = form::data($this->config->bug->form->confirm, $bugID)->add('id', $bugID)->setDefault('confirmed', 1)->get();
|
||||
|
||||
$this->bug->confirm($bug, $kanbanData);
|
||||
|
||||
@@ -501,7 +501,7 @@ class bug extends control
|
||||
|
||||
/* 获取bug信息。 */
|
||||
/* Build bug data. */
|
||||
$bug = form::data($this->config->bug->form->activate)->setDefault('assignedTo', $oldBug->resolvedBy)->add('activatedCount', $oldBug->activatedCount + 1)->add('id', $bugID)->add('resolvedDate', null)->add('closedDate', null)->get();
|
||||
$bug = form::data($this->config->bug->form->activate, $bugID)->setDefault('assignedTo', $oldBug->resolvedBy)->add('activatedCount', $oldBug->activatedCount + 1)->add('id', $bugID)->add('resolvedDate', null)->add('closedDate', null)->get();
|
||||
|
||||
/* 解析看板信息。 */
|
||||
/* Parse kanban information. */
|
||||
@@ -550,7 +550,7 @@ class bug extends control
|
||||
{
|
||||
/* 设置bug信息。 */
|
||||
/* Set bug information. */
|
||||
$bug = form::data($this->config->bug->form->close)->add('id', $bugID)->get();
|
||||
$bug = form::data($this->config->bug->form->close, $bugID)->add('id', $bugID)->get();
|
||||
|
||||
/* 设置额外变量。 */
|
||||
/* Set extra variables. */
|
||||
|
||||
@@ -7,6 +7,7 @@ function changeProduct(event)
|
||||
{
|
||||
const productID = $(event.target).val();
|
||||
if(productID == undefined) return false;
|
||||
if(productID == bug.product) return false;
|
||||
|
||||
if(typeof(changeProductConfirmed) != 'undefined' && !changeProductConfirmed)
|
||||
{
|
||||
@@ -19,7 +20,7 @@ function changeProduct(event)
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#product').val(bug.product); // Revert old product id if confirm is no.
|
||||
$('[name=product]').zui('picker').$.setValue(bug.product); // Revert old product id if confirm is no.
|
||||
}
|
||||
}});
|
||||
}
|
||||
@@ -324,7 +325,7 @@ function loadProjectBuilds(projectID)
|
||||
if(typeof(branch) == 'undefined') branch = 0;
|
||||
|
||||
const productID = $('[name="product"]').val();
|
||||
const oldOpenedBuild = $('[name^="openedBuild"]').val() ? $('[name^="openedBuild"]').val() : 0;
|
||||
const oldOpenedBuild = $('[name^="openedBuild"]').val() ? $('[name^="openedBuild"]').val().toString() : 0;
|
||||
|
||||
if(config.currentMethod == 'create')
|
||||
{
|
||||
@@ -343,14 +344,13 @@ function loadProjectBuilds(projectID)
|
||||
const openedLink = $.createLink('build', 'ajaxGetProjectBuilds', 'projectID=' + projectID + '&productID=' + productID + '&varName=openedBuild&build=' + oldOpenedBuild + '&branch=' + branch);
|
||||
$.getJSON(openedLink, function(data)
|
||||
{
|
||||
let buildID = $('[name^="openedBuild"]').val();
|
||||
let $buildPicker = $('[name^="openedBuild"]').zui('picker');
|
||||
$buildPicker.render({items: data});
|
||||
$buildPicker.$.setValue(buildID);
|
||||
$buildPicker.$.setValue(oldOpenedBuild);
|
||||
loadBuildActions();
|
||||
})
|
||||
|
||||
const oldResolvedBuild = $('[name="resolvedBuild"]').val() ? $('[name="resolvedBuild"]').val() : 0;
|
||||
const oldResolvedBuild = $('[name="resolvedBuild"]').val() ? $('[name="resolvedBuild"]').val().toString() : 0;
|
||||
const resolvedLink = $.createLink('build', 'ajaxGetProductBuilds', 'productID=' + productID + '&varName=resolvedBuild&build=' + oldResolvedBuild + '&branch=' + branch);
|
||||
$.getJSON(resolvedLink, function(data)
|
||||
{
|
||||
@@ -388,7 +388,7 @@ function loadProductBuilds(productID, type = 'normal', buildBox = 'all')
|
||||
const openedLink = $.createLink('build', 'ajaxGetProductBuilds', 'productID=' + productID + '&varName=openedBuild&build=' + bug.openedBuild + '&branch=' + branch + '&type=' + type);
|
||||
$.getJSON(openedLink, function(data)
|
||||
{
|
||||
let buildID = $('[name^="openedBuild"]').val();
|
||||
let buildID = $('[name^="openedBuild"]').val().toString();
|
||||
let $buildPicker = $('[name^="openedBuild"]').zui('picker');
|
||||
$buildPicker.render({items: data});
|
||||
$buildPicker.$.setValue(buildID);
|
||||
@@ -397,7 +397,7 @@ function loadProductBuilds(productID, type = 'normal', buildBox = 'all')
|
||||
|
||||
if(buildBox == 'all' || buildBox == 'resolvedBuild')
|
||||
{
|
||||
const oldResolvedBuild = $('[name="resolvedBuild"]').val() ? $('[name="resolvedBuild"]').val() : 0;
|
||||
const oldResolvedBuild = $('[name="resolvedBuild"]').val() ? $('[name="resolvedBuild"]').val().toString() : 0;
|
||||
const resolvedLink = $.createLink('build', 'ajaxGetProductBuilds', 'productID=' + productID + '&varName=resolvedBuild&build=' + oldResolvedBuild + '&branch=' + branch + '&type=' + type);
|
||||
$.getJSON(resolvedLink, function(data)
|
||||
{
|
||||
@@ -415,7 +415,7 @@ function loadExecutionBuilds(executionID, num)
|
||||
|
||||
let branch = num != '' ? $('#branch' + num).val() : $('[name="branch"]').val();
|
||||
let productID = num != '' ? $('#product' + num).val() : $('[name="product"]').val();
|
||||
const oldOpenedBuild = $('[name^="openedBuild"]' + num).val() ? $('[name^="openedBuild"]' + num).val() : 0;
|
||||
const oldOpenedBuild = $('[name^="openedBuild"]' + num).val() ? $('[name^="openedBuild"]' + num).val().toString() : 0;
|
||||
|
||||
if(typeof(branch) == 'undefined') branch = 'all';
|
||||
if(typeof(productID) == 'undefined') productID = 0;
|
||||
@@ -441,7 +441,7 @@ function loadExecutionBuilds(executionID, num)
|
||||
$buildPicker.$.setValue(oldOpenedBuild);
|
||||
});
|
||||
|
||||
const oldResolvedBuild = $('[name="resolvedBuild"]').val() ? $('[name="resolvedBuild"]').val() : 0;
|
||||
const oldResolvedBuild = $('[name="resolvedBuild"]').val() ? $('[name="resolvedBuild"]').val().toString() : 0;
|
||||
const resolvedLink = $.createLink('build', 'ajaxGetProductBuilds', 'productID=' + productID + '&varName=resolvedBuild&build=' + oldResolvedBuild + '&branch=' + branch);
|
||||
$.getJSON(resolvedLink, function(data)
|
||||
{
|
||||
@@ -687,3 +687,22 @@ let checkHasCheckedData = function(item, checkedValue)
|
||||
{
|
||||
return item.value == checkedValue;
|
||||
};
|
||||
|
||||
function loadBuilds()
|
||||
{
|
||||
const productID = $('[name="product"]').val();
|
||||
const projectID = $('[name="project"]').val() == 'undefined' ? 0 : $('[name="project"]').val();
|
||||
const executionID = $('[name="execution"]').val() == 'undefined' ? 0 : $('[name="execution"]').val();
|
||||
if(executionID)
|
||||
{
|
||||
loadExecutionBuilds(executionID);
|
||||
}
|
||||
else if(projectID)
|
||||
{
|
||||
loadProjectBuilds(projectID);
|
||||
}
|
||||
else
|
||||
{
|
||||
loadProductBuilds(productID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,6 +440,7 @@ $lang->bug->notice->delayWarning = " <strong class='text-danger'> Delay
|
||||
$lang->bug->notice->skipNotActive = "Bug %s has/have been resolved or closed, it won't be changed again";
|
||||
|
||||
$lang->bug->error = new stdclass();
|
||||
$lang->bug->error->notExist = "Bug doesn't exist.";
|
||||
$lang->bug->error->cannotActivate = 'Bugs with a status other than Resolved or Closed cannot be activated.';
|
||||
$lang->bug->error->stepsNotEmpty = "The reproduction step cannot be empty.";
|
||||
$lang->bug->error->notExist = "Bug doesn't exist.";
|
||||
$lang->bug->error->cannotActivate = 'Bugs with a status other than Resolved or Closed cannot be activated.';
|
||||
$lang->bug->error->stepsNotEmpty = "The reproduction step cannot be empty.";
|
||||
$lang->bug->error->duplicateBugNotExist = 'The duplicate bug does not exist.';
|
||||
|
||||
@@ -440,6 +440,7 @@ $lang->bug->notice->delayWarning = " <strong class='text-danger'> Delay
|
||||
$lang->bug->notice->skipNotActive = "Bug %s has/have been resolved or closed, it won't be changed again";
|
||||
|
||||
$lang->bug->error = new stdclass();
|
||||
$lang->bug->error->notExist = "Bug doesn't exist.";
|
||||
$lang->bug->error->cannotActivate = 'Bugs with a status other than Resolved or Closed cannot be activated.';
|
||||
$lang->bug->error->stepsNotEmpty = "The reproduction step cannot be empty.";
|
||||
$lang->bug->error->notExist = "Bug doesn't exist.";
|
||||
$lang->bug->error->cannotActivate = 'Bugs with a status other than Resolved or Closed cannot be activated.';
|
||||
$lang->bug->error->stepsNotEmpty = "The reproduction step cannot be empty.";
|
||||
$lang->bug->error->duplicateBugNotExist = 'The duplicate bug does not exist.';
|
||||
|
||||
@@ -440,6 +440,7 @@ $lang->bug->notice->delayWarning = " <strong class='text-danger'> Retar
|
||||
$lang->bug->notice->skipNotActive = "Bug %s has/have been resolved or closed, it won't be changed again";
|
||||
|
||||
$lang->bug->error = new stdclass();
|
||||
$lang->bug->error->notExist = "Bug doesn't exist.";
|
||||
$lang->bug->error->cannotActivate = 'Bugs with a status other than Resolved or Closed cannot be activated.';
|
||||
$lang->bug->error->stepsNotEmpty = "The reproduction step cannot be empty.";
|
||||
$lang->bug->error->notExist = "Bug doesn't exist.";
|
||||
$lang->bug->error->cannotActivate = 'Bugs with a status other than Resolved or Closed cannot be activated.';
|
||||
$lang->bug->error->stepsNotEmpty = "The reproduction step cannot be empty.";
|
||||
$lang->bug->error->duplicateBugNotExist = 'The duplicate bug does not exist.';
|
||||
|
||||
@@ -440,6 +440,7 @@ $lang->bug->notice->delayWarning = " <strong class='text-danger'> 延
|
||||
$lang->bug->notice->skipNotActive = "Bug %s 已解决或已关闭,不再修改。";
|
||||
|
||||
$lang->bug->error = new stdclass();
|
||||
$lang->bug->error->notExist = 'Bug不存在。';
|
||||
$lang->bug->error->cannotActivate = '状态不是已解决或已关闭的Bug不能激活。';
|
||||
$lang->bug->error->stepsNotEmpty = "重现步骤不能为空。";
|
||||
$lang->bug->error->notExist = 'Bug不存在。';
|
||||
$lang->bug->error->cannotActivate = '状态不是已解决或已关闭的Bug不能激活。';
|
||||
$lang->bug->error->stepsNotEmpty = "重现步骤不能为空。";
|
||||
$lang->bug->error->duplicateBugNotExist = '重复Bug不存在。';
|
||||
|
||||
+11
-1
@@ -410,6 +410,15 @@ class bugModel extends model
|
||||
{
|
||||
/* Get old bug. */
|
||||
$oldBug = $this->getById($bug->id);
|
||||
if(!empty($bug->duplicateBug))
|
||||
{
|
||||
$duplicateBug = $this->fetchByID($bug->duplicateBug);
|
||||
if(!$duplicateBug || $duplicateBug->deleted == '1' || $duplicateBug->product != $oldBug->product)
|
||||
{
|
||||
dao::$errors['duplicateBug'][] = $this->lang->bug->error->duplicateBugNotExist;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update bug. */
|
||||
$this->dao->update(TABLE_BUG)->data($bug, 'buildName,createBuild,buildExecution,comment')
|
||||
@@ -1705,7 +1714,8 @@ class bugModel extends model
|
||||
->fetchPairs('id');
|
||||
if(empty($story)) $story = array(0);
|
||||
|
||||
$bugQuery = preg_replace("/`story`[ ]+(NOT[ ]*)?LIKE[ ]+'%$searchValue%'/Ui", '`story` $1 IN (' . implode(',', $story) .')', $bugQuery);
|
||||
$searchValue = preg_quote($searchValue, '/');
|
||||
$bugQuery = preg_replace("/`story`[ ]+(NOT[ ]*)?LIKE[ ]+'%$searchValue%'/Ui", '`story` $1 IN (' . implode(',', $story) .')', $bugQuery);
|
||||
}
|
||||
}
|
||||
$bugQuery .= ' AND `story` != 0';
|
||||
|
||||
@@ -113,7 +113,7 @@ class confirmBugTester extends tester
|
||||
{
|
||||
if(empty($bugTitle) || !is_object($list)) return $this->failed('获取bug标题失败');
|
||||
|
||||
$bugTitleLists = $list->dom->bugTitleList->getElementList($list->dom->page->xpath['bugTitleList']);
|
||||
$bugTitleLists = $list->dom->getElementList($list->dom->xpath['bugTitleList']);
|
||||
$bugList = array_map(function($element){return $element->getText();}, $bugTitleLists->element);
|
||||
if(!in_array($bugTitle, $bugList)) return $this->success('操作bug成功');
|
||||
return $this->failed('操作bug失败');
|
||||
|
||||
@@ -16,11 +16,11 @@ include($this->app->getModuleRoot() . 'ai/ui/inputinject.html.php');
|
||||
$fields = useFields('bug.create');
|
||||
if(!empty($executionType) && $executionType == 'kanban') $fields->merge('bug.kanban');
|
||||
|
||||
$fields->autoLoad('product', array('items' => 'product,module,openedBuild,execution,project,story,task,assignedTo', 'updateOrders' => true))
|
||||
->autoLoad('branch', 'module,openedBuild,execution,project,story,task,assignedTo')
|
||||
$fields->autoLoad('product', array('items' => 'product,module,execution,project,story,task,assignedTo', 'updateOrders' => true))
|
||||
->autoLoad('branch', 'module,execution,project,story,task,assignedTo')
|
||||
->autoLoad('module', 'assignedTo,story')
|
||||
->autoLoad('project', 'project,openedBuild,execution,story,task,assignedTo,injection,identify')
|
||||
->autoLoad('execution', 'execution,openedBuild,story,task,assignedTo')
|
||||
->autoLoad('project', 'project,execution,story,task,assignedTo,injection,identify')
|
||||
->autoLoad('execution', 'execution,story,task,assignedTo')
|
||||
->autoLoad('allBuilds', 'openedBuild')
|
||||
->autoLoad('allUsers', 'assignedTo')
|
||||
->autoLoad('region', 'lane');
|
||||
@@ -36,6 +36,7 @@ jsVar('projectExecutionPairs', $projectExecutionPairs);
|
||||
|
||||
formGridPanel
|
||||
(
|
||||
on::change('[name="product"], [name="branch"], [name="project"], [name="execution"]', 'loadBuilds'),
|
||||
set::title($lang->bug->create),
|
||||
set::fields($fields),
|
||||
set::loadUrl($loadUrl)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user