Merge branch 'main' into zentao_menu

This commit is contained in:
dingyongliang
2024-08-28 14:13:55 +08:00
369 changed files with 9457 additions and 1255 deletions
+2 -2
View File
@@ -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/*
+1 -1
View File
@@ -1 +1 @@
20.5
20.6
+6 -5
View File
@@ -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'));
}
}
}
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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();
+5 -5
View File
@@ -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')
{
+2 -2
View File
@@ -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');
+2 -1
View File
@@ -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', ''));
+1 -1
View File
@@ -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')
{
+1
View File
@@ -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));
+1 -1
View File
@@ -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();
+1 -1
View File
@@ -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();
+6 -6
View File
@@ -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;
+1
View File
@@ -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');
+1 -1
View File
@@ -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');
+2 -2
View File
@@ -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);
+1 -1
View File
@@ -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);
+6 -3
View File
@@ -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);
+1 -1
View File
@@ -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);
+1 -2
View File
@@ -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');
}
}
+3 -4
View File
@@ -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'));
}
+1 -1
View File
@@ -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));
}
}
+5 -16
View File
@@ -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'));
}
}
+13
View File
@@ -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("&amp;quot;", "&amp;#039;", "&amp;lt;", "&amp;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'));
}
+2 -2
View File
@@ -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
View File
@@ -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.
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
-9
View File
@@ -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');
+37
View File
@@ -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
View File
@@ -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');
+41
View File
@@ -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
完成的需求:
开源版:
+22 -2
View File
@@ -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();
+4 -3
View File
@@ -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);
}
+1 -1
View File
@@ -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
View File
@@ -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)
{
+5 -3
View File
@@ -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)
+1 -1
View File
@@ -43,7 +43,7 @@ class date
*/
public static function today()
{
return date(DT_DATE2, time());
return date(DT_DATE1, time());
}
/**
+11 -5
View File
@@ -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';
+2
View File
@@ -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());}
+6 -4
View File
@@ -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();
+1 -1
View File
@@ -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')
),
+23 -7
View File
@@ -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)})'
)
);
}
}
+2 -2
View File
@@ -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())))
);
}
}
+3 -3
View File
@@ -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);
+3 -1
View File
@@ -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')
{
+12 -6
View File
@@ -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()))
);
}
}
+2 -6
View File
@@ -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'])
+3 -5
View File
@@ -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),
+1
View File
@@ -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));
}
+56 -1
View File
@@ -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);
+2 -1
View File
@@ -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')
+2 -2
View File
@@ -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;
}
+11 -4
View File
@@ -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)')})
+1
View File
@@ -12,6 +12,7 @@ class panel extends wg
'size?: "sm"|"lg"', // 额外尺寸。
'title?: string', // 标题。
'shadow?: bool=true', // 阴影效果。
'titleIcon?: string', // 标题图标。
'titleClass?: string', // 标题类名。
'titleProps?: array', // 标题属性。
'headingClass?: string', // 标题栏类名。
+1
View File
@@ -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),
+2
View File
@@ -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'));
+5 -5
View File
@@ -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))
+4 -3
View File
@@ -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);
}
}
+11 -8
View File
@@ -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)
)
)
);
+35
View File
@@ -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;}
+409
View File
@@ -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});
+63
View File
@@ -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')))
)
);
}
}
+8
View File
@@ -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);}
+23
View File
@@ -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);
+169
View File
@@ -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 -1
View File
@@ -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';}
+57
View File
@@ -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);
}
}
+163 -7
View File
@@ -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
(
+1 -1
View File
@@ -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)));
+4 -2
View File
@@ -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();
}
+2 -1
View File
@@ -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
+17 -26
View File
@@ -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 -1
View File
@@ -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';}
+2 -2
View File
@@ -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)
),
+2 -2
View File
@@ -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')
{
+1 -1
View File
@@ -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&param=skipParent,forStory,' + config.currentMethod);
planLink = $.createLink('product', 'ajaxGetPlans', 'productID=' + productID + '&branch=' + branch + '&params=unexpired,noclosed&skipParent=true');
if(branchIndex > 0)
{
var $planIdBox = $('.addBranchesBox'+ branchIndex +' #planIdBox');
+2 -29
View File
@@ -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'
+9
View File
@@ -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);
+20
View File
@@ -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');
}
};
+3 -5
View File
@@ -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);
+4
View File
@@ -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();
+6 -6
View File
@@ -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);
+1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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)
+6 -2
View File
@@ -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;
});
});
+2 -2
View File
@@ -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);
+2 -2
View File
@@ -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;
+5 -5
View File
@@ -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. */
+28 -9
View File
@@ -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);
}
}
+4 -3
View File
@@ -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.';
+4 -3
View File
@@ -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.';
+4 -3
View File
@@ -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.';
+4 -3
View File
@@ -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
View File
@@ -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';
+1 -1
View File
@@ -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失败');
+5 -4
View File
@@ -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