v1.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. <?php
  2. /**
  3. * The formPanel widget class file of zin module of ZenTaoPMS.
  4. *
  5. * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
  6. * @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
  7. * @author sunhao<sunhao@easycorp.ltd>
  8. * @package zin
  9. * @link http://www.zentao.net
  10. */
  11. namespace zin;
  12. require_once dirname(__DIR__) . DS . 'panel' . DS . 'v1.php';
  13. require_once dirname(__DIR__) . DS . 'formgroup' . DS . 'v1.php';
  14. require_once dirname(__DIR__) . DS . 'formrow' . DS . 'v1.php';
  15. require_once dirname(__DIR__) . DS . 'form' . DS . 'v1.php';
  16. require_once dirname(__DIR__) . DS . 'formbatch' . DS . 'v1.php';
  17. /**
  18. * 表单面板(formPanel)部件类。
  19. * The form panel widget class.
  20. *
  21. * @author Hao Sun
  22. */
  23. class formPanel extends panel
  24. {
  25. /**
  26. * Define widget properties.
  27. *
  28. * @var array
  29. * @access protected
  30. */
  31. protected static $defineProps = array(
  32. 'class?: string="panel-form"', // 类名。
  33. 'size?: string="lg"', // 额外尺寸。
  34. 'formID?: string="$GID"', // 表单 ID,如果指定为 '$AUTO',则自动生成 form-$moduleName-$methodName。
  35. 'formClass?: string', // 表单样式。
  36. 'method?: "get"|"post"="post"', // 表单提交方式。
  37. 'enctype?: string', // 表单提交类型。
  38. 'tagName?: string="form"', // 表单标签名。
  39. 'url?: string', // 表单提交地址。
  40. 'actions?: array', // 表单操作按钮,如果不指定则使用默认行为的 “保存” 和 “返回” 按钮。
  41. 'actionsClass?: string="form-group no-label"', // 表单操作按钮栏类名。
  42. 'stickyActions?: array|bool=false', // 是否固定操作按钮栏。
  43. 'target?: string="ajax"', // 表单提交目标,如果是 `'ajax'` 提交则为 ajax,在禅道中除非特殊目的,都使用 ajax 进行提交。
  44. 'submitBtnText?: string', // 表单提交按钮文本,如果不指定则使用 `$lang->save` 的值。
  45. 'cancelBtnText?: string', // 表单取消按钮文本,如果不指定则使用 `$lang->goback` 的值。
  46. 'items?: array', // 使用一个列定义对象数组来定义表单项。
  47. 'fields?: string|array|fieldList', // 表单字段配置。
  48. 'fullModeOrders?: string|array', // 完整模式下字段显示顺序。
  49. 'layout?: string="horz"', // 表单布局,可选值为:'horz'、'grid' 和 `normal`。
  50. 'labelWidth?: int', // 标签宽度,单位为像素。
  51. 'batch?: bool', // 是否为批量操作表单。
  52. 'shadow?: bool=false', // 是否显示阴影层。
  53. 'width?: string', // 最大宽度。
  54. 'modeSwitcher?: bool', // 是否显示表单模式按钮。
  55. 'data?: array|object', // 表单项值默认数据。
  56. 'labelData?: array|object', // 表单项标签默认数据。
  57. 'loadUrl?: string', // 动态更新 URL。
  58. 'autoLoad?: array', // 动态更新策略。
  59. 'defaultMode?: string="lite"', // 默认表单模式(lite: 简洁版,full: 完整版)。
  60. 'foldableItems?: array|string', // 可折叠的表单项。
  61. 'pinnedItems?: array|string', // 固定显示的表单项。
  62. 'customBtn?: array|bool', // 是否显示表单自定义按钮。
  63. 'customFields?: array=[]', // @deprecated 自定义表单项。
  64. 'showExtra?: bool=true' // 是否显示工作流字段。
  65. );
  66. public static function getPageJS()
  67. {
  68. return file_get_contents(__DIR__ . DS . 'js' . DS . 'v1.js');
  69. }
  70. protected function getData()
  71. {
  72. global $app;
  73. $moduleName = $app->getModuleName();
  74. $methodName = $app->getMethodName();
  75. if($moduleName == 'caselib') return data('lib');
  76. if($moduleName == 'flow') return data('data');
  77. if($moduleName == 'productplan') return data('plan');
  78. if($moduleName == 'projectrelease') return data('release');
  79. if($moduleName == 'projectbuild') return data('build');
  80. if($moduleName == 'cm') return data('baseline');
  81. if($moduleName == 'project' && ($methodName == 'create' or $methodName == 'createtemplate') && data('copyProject')) return data('copyProject');
  82. return data($moduleName);
  83. }
  84. protected function getModuleAndMethodForExtend()
  85. {
  86. global $app;
  87. $moduleName = $app->rawModule;
  88. $methodName = $app->rawMethod;
  89. /* 项目发布和项目版本用自己的工作流。 */
  90. if($moduleName == 'projectrelease') $moduleName = 'release';
  91. if($moduleName == 'projectplan') $moduleName = 'productplan';
  92. if($moduleName == 'projectbuild')
  93. {
  94. $moduleName = 'build';
  95. if($methodName == 'browse')
  96. {
  97. $moduleName = 'execution';
  98. $methodName = 'build';
  99. }
  100. }
  101. if($moduleName == 'project' && $methodName == 'createtemplate') $methodName = 'create';
  102. if($moduleName == 'project' && $methodName == 'edittemplate') $methodName = 'edit';
  103. /* 反馈转化。 */
  104. if($moduleName == 'feedback')
  105. {
  106. if($methodName == 'tostory')
  107. {
  108. $moduleName = 'story';
  109. $methodName = 'create';
  110. }
  111. elseif($methodName == 'touserstory')
  112. {
  113. $moduleName = 'requirement';
  114. $methodName = 'create';
  115. }
  116. elseif($methodName == 'toepic')
  117. {
  118. $moduleName = 'epic';
  119. $methodName = 'create';
  120. }
  121. elseif($methodName == 'toticket')
  122. {
  123. $moduleName = 'ticket';
  124. $methodName = 'create';
  125. }
  126. }
  127. if($moduleName == 'ticket')
  128. {
  129. if($methodName == 'createstory')
  130. {
  131. $moduleName = 'story';
  132. $methodName = 'create';
  133. }
  134. elseif($methodName == 'createbug')
  135. {
  136. $moduleName = 'bug';
  137. $methodName = 'create';
  138. }
  139. }
  140. return array($moduleName, $methodName);
  141. }
  142. protected function created()
  143. {
  144. $fields = $this->prop('fields');
  145. if(is_object($fields))
  146. {
  147. global $app;
  148. list($moduleName, $methodName) = $this->getModuleAndMethodForExtend();
  149. $fields = $app->control->appendExtendFields($fields, $moduleName, $methodName, $this->getData());
  150. $this->setProp('fields', $fields);
  151. }
  152. $customFields = $this->prop('customFields');
  153. if($customFields === true)
  154. {
  155. global $app, $config, $lang;
  156. $module = $app->rawModule;
  157. $method = $app->rawMethod;
  158. $app->loadLang($module);
  159. $app->loadModuleConfig($module);
  160. $key = $method . 'Fields';
  161. $listFields = array();
  162. $listFieldsKey = 'custom' . ucfirst($key);
  163. if(!empty($config->$module->$listFieldsKey)) $listFields = explode(',', $config->$module->$listFieldsKey);
  164. if(!empty($config->$module->list->$listFieldsKey)) $listFields = explode(',', $config->$module->list->$listFieldsKey);
  165. if(empty($listFields))
  166. {
  167. $this->setProp('customFields', array());
  168. return false;
  169. }
  170. $fields = array();
  171. foreach($listFields as $field) $fields[$field] = $lang->$module->$field;
  172. $showFields = explode(',', $config->$module->custom->$key);
  173. $customFields = array('list' => $fields, 'show' => $showFields, 'key' => $key);
  174. $this->setProp('customFields', $customFields);
  175. }
  176. /* 后台设置字段为必填时,在自定义表单项设置中不显示该字段。*/
  177. /* required fields not showing up in custom form settings when enforced by backend. */
  178. $customFields = $this->prop('customFields');
  179. if($customFields)
  180. {
  181. global $app, $config;
  182. $module = $app->rawModule;
  183. $method = $app->rawMethod;
  184. if($method == 'batchedit') $method = 'edit';
  185. if($method == 'batchcreate') $method = 'create';
  186. if(!empty($config->$module->$method->requiredFields))
  187. {
  188. $listFields = zget($customFields, 'list', array());
  189. $showFields = zget($customFields, 'show', array());
  190. $key = zget($customFields, 'key', $app->rawMethod);
  191. foreach($listFields as $field => $name)
  192. {
  193. if(strpos(",{$config->$module->$method->requiredFields},", ",$field,") !== false) unset($listFields[$field]);
  194. }
  195. $showFields = array_merge($showFields, explode(',', $config->$module->$method->requiredFields));
  196. $customFields = array('list' => $listFields, 'show' => $showFields, 'key' => $key);
  197. $this->setProp('customFields', $customFields);
  198. }
  199. }
  200. if($this->prop('modeSwitcher'))
  201. {
  202. global $lang;
  203. $modeSwitcher = btnGroup
  204. (
  205. set::size('sm'),
  206. btn
  207. (
  208. setClass('gray-300-outline text-sm rounded-full btn-lite-form'),
  209. $lang->liteMode
  210. ),
  211. btn
  212. (
  213. setClass('gray-300-outline text-sm rounded-full btn-full-form'),
  214. $lang->fullMode
  215. )
  216. );
  217. $this->addToBlock('headingActions', $modeSwitcher);
  218. }
  219. }
  220. protected function getHeadingActions()
  221. {
  222. $actions = parent::getHeadingActions();
  223. /* Custom fields. */
  224. $customFields = $this->prop('customFields', array());
  225. if($customFields)
  226. {
  227. global $app;
  228. $listFields = zget($customFields, 'list', array());
  229. $showFields = zget($customFields, 'show', array());
  230. $key = zget($customFields, 'key', $app->rawMethod);
  231. if($listFields && $key)
  232. {
  233. /* Custom button submit params. */
  234. $urlParams = $this->prop('customUrlParams') ? $this->prop('customUrlParams') : "module={$app->rawModule}&section=custom&key={$key}";
  235. $actions[] = formSettingBtn(set::customFields(array('list' => $listFields, 'show' => $showFields)), set::urlParams(zget($customFields, 'urlParams', $urlParams)));
  236. }
  237. }
  238. return $actions;
  239. }
  240. protected function buildExtraMain()
  241. {
  242. global $app;
  243. $layout = $this->prop('layout');
  244. if($layout == 'grid') return null;
  245. list($moduleName, $methodName) = $this->getModuleAndMethodForExtend();
  246. $data = $this->getData();
  247. $fields = $app->control->appendExtendForm('info', $data, $moduleName, $methodName);
  248. $extraMain = array();
  249. foreach($fields as $field)
  250. {
  251. $extraMain[] = formGroup
  252. (
  253. $field->control == 'file' && $data->files ? fileList
  254. (
  255. set::files($data->files),
  256. set::extra($field->field),
  257. set::fieldset(false),
  258. set::showEdit(true),
  259. set::showDelete(true)
  260. ) : null,
  261. set::width($field->width),
  262. set::label($field->name),
  263. set::labelProps($field->control['control'] == 'picker' ? array('required' => $field->required) : null),
  264. set::id($field->field),
  265. set::name($field->field),
  266. set::required($field->control['control'] == 'picker' ? false : $field->required),
  267. set::disabled((bool)$field->readonly),
  268. set::control($field->control),
  269. set::items($field->items),
  270. set::value($field->value),
  271. set::placeholder($field->placeholder)
  272. );
  273. if((bool)$field->readonly) $extraMain[] = formHidden($field->field, $field->value);
  274. }
  275. return $extraMain;
  276. }
  277. protected function buildExtraBatchItem()
  278. {
  279. global $app;
  280. list($moduleName, $methodName) = $this->getModuleAndMethodForExtend();
  281. $data = $this->getData();
  282. $fields = $app->control->appendExtendForm('info', $data, $moduleName, $methodName);
  283. $formBatchItem = array();
  284. foreach($fields as $field)
  285. {
  286. $value = isset($field->defaultValue) && $field->defaultValue != '' ? $field->defaultValue : $field->default;
  287. $value = isset($data->{$field->field}) ? $data->{$field->field} : $value;
  288. $formBatchItem[] = formBatchItem
  289. (
  290. set::name($field->field),
  291. set::label($field->name),
  292. set::required($field->required),
  293. set::control($field->control),
  294. set::items($field->items),
  295. set::width('200px'),
  296. set::value($value),
  297. set::placeholder($field->placeholder)
  298. );
  299. }
  300. return $formBatchItem;
  301. }
  302. /**
  303. * Build form widget by mode.
  304. *
  305. * @access protected
  306. * @return node
  307. */
  308. protected function buildForm()
  309. {
  310. $fields = $this->prop('fields', array());
  311. $customFields = $this->prop('customFields', array());
  312. $listFields = zget($customFields, 'list', array());
  313. $showFields = zget($customFields, 'show', array());
  314. $hiddenFields = $listFields && $showFields ? array_values(array_diff(array_keys($listFields), $showFields)) : array();
  315. $formID = $this->prop('formID');
  316. if($this->prop('batch'))
  317. {
  318. $props = formBatch::definedPropsList();
  319. unset($props['id']);
  320. return new formBatch
  321. (
  322. set::id($formID),
  323. set($this->props->pick(array_keys($props))),
  324. $this->children(),
  325. $this->prop('showExtra') ? $this->buildExtraBatchItem() : null,
  326. set::hiddenFields($hiddenFields),
  327. jsVar('formBatch', true),
  328. $hiddenFields ? jsVar('hiddenFields', $hiddenFields) : null
  329. );
  330. }
  331. $props = array_keys(form::definedPropsList());
  332. $formProps = array();
  333. foreach($props as $propName)
  334. {
  335. if($this->hasProp($propName) && $propName !== 'id') $formProps[] = $propName;
  336. }
  337. return new form
  338. (
  339. set::id($formID),
  340. set::className($this->prop('formClass')),
  341. set($this->props->pick($formProps)),
  342. $this->children(),
  343. $this->prop('showExtra') && !$fields ? $this->buildExtraMain() : null,
  344. $hiddenFields ? jsVar('hiddenFields', $hiddenFields) : null
  345. );
  346. }
  347. /**
  348. * Build widget props.
  349. *
  350. * @access protected
  351. * @return array
  352. */
  353. protected function buildProps()
  354. {
  355. list($width, $batch, $shadow, $defaultMode) = $this->prop(array('width', 'batch', 'shadow', 'defaultMode'));
  356. $props = parent::buildProps();
  357. $props[] = setClass("is-$defaultMode");
  358. if($width) $props[] = setCssVar('--zt-panel-form-max-width', $width);
  359. elseif($batch) $props[] = setCssVar('--zt-panel-form-max-width', 'auto');
  360. if($shadow) $props[] = setClass('shadow');
  361. return $props;
  362. }
  363. /**
  364. * Build panel body.
  365. *
  366. * @access protected
  367. * @return node
  368. */
  369. protected function buildBody()
  370. {
  371. global $app;
  372. list($moduleName, $methodName) = $this->getModuleAndMethodForExtend();
  373. return div
  374. (
  375. setClass('panel-body ' . $this->prop('bodyClass')),
  376. set($this->prop('bodyProps')),
  377. $this->buildContainer($this->buildForm()),
  378. html($app->control->appendExtendCssAndJS($moduleName, $methodName, $this->getData()))
  379. );
  380. }
  381. }