TemplateMixedPublish.vue 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888
  1. <template>
  2. <el-dialog
  3. :visible="innerVisible"
  4. @close="handleClose"
  5. @open="initData"
  6. :close-on-click-modal="false"
  7. :close-on-press-escape="false"
  8. center
  9. title="通过模板发布考核"
  10. width="600px"
  11. >
  12. <el-carousel
  13. v-loading="loading"
  14. :autoplay="false"
  15. :loop="false"
  16. :initial-index="0"
  17. height="500px"
  18. indicator-position="none"
  19. arrow="never"
  20. ref="carousel"
  21. >
  22. <el-carousel-item
  23. style="overflow-y: auto;"
  24. >
  25. <el-alert
  26. :closable="false"
  27. type="info"
  28. title="考核基础信息,正确描述考核有助于管理员快速了解考核性质。考核分类可帮助管理员快速找到考核。同一类型的考核更有可比性"
  29. style="margin-bottom: 10px;"
  30. />
  31. <el-form
  32. :model="publishData"
  33. label-width="100px"
  34. >
  35. <el-form-item label="考核名" prop="title">
  36. <el-input
  37. v-model="publishData.title"
  38. maxlength="20"
  39. style="width: 400px"
  40. />
  41. </el-form-item>
  42. <el-form-item label="考核分类">
  43. <el-select v-model="publishData.cateId" >
  44. <el-option
  45. v-for="cate in cateList"
  46. :key="cate.cateId"
  47. :label="cate.name"
  48. :value="cate.cateId"
  49. />
  50. </el-select>
  51. </el-form-item>
  52. <el-card class="card-cycle">
  53. <el-form-item label="周期种类">
  54. <el-radio-group
  55. v-model="publishData.cycleType"
  56. @change="changeCycle"
  57. >
  58. <el-radio-button label="0">自定义</el-radio-button>
  59. <el-radio-button label="1">年度</el-radio-button>
  60. <el-radio-button label="2">半年度 </el-radio-button>
  61. <el-radio-button label="3">季度</el-radio-button>
  62. <el-radio-button label="4">月度</el-radio-button>
  63. </el-radio-group>
  64. </el-form-item>
  65. <el-form-item v-if="publishData.cycleType === '0'" label="考核时间" prop="startDate">
  66. <el-date-picker
  67. v-model="dateRange"
  68. type="daterange"
  69. unlink-panels
  70. range-separator="至"
  71. start-placeholder="开始日期"
  72. end-placeholder="结束日期"
  73. :key="1"
  74. />
  75. </el-form-item>
  76. <el-form-item v-if="publishData.cycleType === '1'" label="选择年份" prop="year">
  77. <el-date-picker
  78. v-model="year"
  79. type="year"
  80. value-format="yyyy"
  81. placeholder="选择年"
  82. :key="2"
  83. />
  84. </el-form-item>
  85. <el-form-item
  86. v-if="publishData.cycleType === '2' || publishData.cycleType === '3'"
  87. label="日期区间"
  88. >
  89. <SelectCircle :dateParameter="dateParameter" :dateOptions="dateOptions" @confirm="dateConfirm"
  90. :id="1">
  91. <div class="flex-box-ce cursor">
  92. <div>{{ dateParameter.year }}</div>
  93. <div style="margin: 0 10px;">{{ dateParameter.name }}</div>
  94. <i class="el-icon-caret-bottom fontColorC"></i>
  95. </div>
  96. </SelectCircle>
  97. </el-form-item>
  98. <el-form-item v-if="publishData.cycleType === '4'" label="选择月份" prop="month">
  99. <el-date-picker
  100. v-model="month"
  101. type="month"
  102. placeholder="月份"
  103. value-format="yyyy-MM"
  104. :key="3"
  105. />
  106. </el-form-item>
  107. <el-form-item label="考核周期">
  108. <el-link
  109. v-if="startDate && endDate"
  110. type="primary"
  111. >
  112. {{ startDate }} 至 {{ endDate }}
  113. </el-link>
  114. <el-link
  115. v-else
  116. type="warning"
  117. >请指定周期</el-link>
  118. </el-form-item>
  119. </el-card>
  120. </el-form>
  121. </el-carousel-item>
  122. <el-carousel-item
  123. style="overflow-y: auto;"
  124. >
  125. <el-alert
  126. :closable="false"
  127. type="info"
  128. title="关联OKR目标可以帮助考核评审进一步跟踪过程,提高考核的准确性"
  129. style="margin-bottom: 10px;"
  130. />
  131. <el-card
  132. style="height: 450px;"
  133. >
  134. <el-row
  135. style="height: 50px;"
  136. type="flex"
  137. justify="space-between"
  138. align="middle"
  139. >
  140. <el-col :span="6">
  141. <span>关联OKR目标</span>
  142. </el-col>
  143. <el-col :span="2">
  144. <el-button
  145. type="text"
  146. icon="el-icon-plus"
  147. @click.stop="showTargetSearch = true"
  148. />
  149. </el-col>
  150. </el-row>
  151. <div
  152. style="height: 400px;overflow-y: auto;padding-bottom: 20px;"
  153. >
  154. <el-alert
  155. v-for="item in selectedOkrs"
  156. :key="item.id"
  157. type="success"
  158. :title="item.name"
  159. :closable="false"
  160. />
  161. </div>
  162. </el-card>
  163. </el-carousel-item>
  164. <el-carousel-item style="overflow-y: auto;">
  165. <el-alert
  166. :closable="false"
  167. type="info"
  168. style="margin-bottom: 10px;"
  169. title="按岗位、职能、角色等方式根据团队实际情况定义指标模板,为不同模板指定人员发起考核更灵活"
  170. />
  171. <el-row
  172. type="flex"
  173. justify="end"
  174. align="middle"
  175. style="height: 30px;"
  176. >
  177. <el-col :span="2">
  178. <el-button
  179. type="text"
  180. icon="el-icon-plus"
  181. @click.stop="showTemplateSearch = true"
  182. />
  183. </el-col>
  184. </el-row>
  185. <el-table
  186. :data="templateList"
  187. height="400px"
  188. >
  189. <el-table-column
  190. prop="title"
  191. label="模板"
  192. />
  193. <el-table-column
  194. prop="employees"
  195. label="考核人员"
  196. show-overflow-tooltip
  197. >
  198. <template slot-scope="scope">
  199. <el-link
  200. v-for="employee in scope.row.employees"
  201. :key="employee.employeeId"
  202. style="margin: 5px;"
  203. type="primary"
  204. >
  205. {{ employee.name }}
  206. </el-link>
  207. </template>
  208. </el-table-column>
  209. <el-table-column
  210. prop="others"
  211. width="100"
  212. >
  213. <template slot-scope="scope">
  214. <el-button type="primary" size="mini" @click="openEmployeeSelector(scope.row)">绑定人员</el-button>
  215. </template>
  216. </el-table-column>
  217. </el-table>
  218. </el-carousel-item>
  219. <el-carousel-item style="overflow-y: auto;">
  220. <el-alert
  221. :closable="false"
  222. type="info"
  223. style="margin-bottom: 10px;"
  224. title="面谈流程可以为每个人考核进行复盘跟进,添加跟踪任务等。对个人提升很有帮助"
  225. />
  226. <el-form
  227. label-width="100px"
  228. >
  229. <el-form-item label="启用">
  230. <el-switch v-model="interviewNode.enable"></el-switch>
  231. </el-form-item>
  232. <el-form-item label="处理人">
  233. <el-radio-group
  234. v-model="interviewNode.assigneeType"
  235. :disabled="!interviewNode.enable"
  236. size="mini"
  237. >
  238. <el-radio-button label="leader">管理员</el-radio-button>
  239. <el-radio-button label="user">指定人员</el-radio-button>
  240. <el-radio-button label="self">被考核人</el-radio-button>
  241. <el-radio-button label="post">岗位</el-radio-button>
  242. <el-radio-button label="deptLeader">部门负责人</el-radio-button>
  243. </el-radio-group>
  244. </el-form-item>
  245. <el-form-item v-if="interviewNode.assigneeType === 'leader'">
  246. <el-select
  247. v-model="interviewNode.leaderLevel"
  248. placeholder="请选择管理员"
  249. :disabled="!interviewNode.enable"
  250. key="leader"
  251. filterable
  252. >
  253. <el-option
  254. v-for="item in levelOptions"
  255. :key="item.value"
  256. :label="item.label"
  257. :value="item.value"
  258. />
  259. </el-select>
  260. </el-form-item>
  261. <el-form-item v-if="interviewNode.assigneeType === 'user'">
  262. <el-select
  263. v-model="assigneeUsers"
  264. placeholder="请选择指定人员"
  265. multiple
  266. key="user"
  267. :disabled="!interviewNode.enable"
  268. filterable
  269. style="width: 400px;"
  270. >
  271. <el-option
  272. v-for="item in employees"
  273. :key="item.id"
  274. :label="item.name"
  275. :value="item.id"
  276. />
  277. </el-select>
  278. </el-form-item>
  279. <el-form-item v-if="interviewNode.assigneeType === 'post'">
  280. <el-select
  281. v-model="assigneePosts"
  282. placeholder="请选择岗位"
  283. multiple
  284. key="post"
  285. :disabled="!interviewNode.enable"
  286. filterable
  287. style="width: 400px;"
  288. >
  289. <el-option
  290. v-for="item in postList"
  291. :key="item.id"
  292. :label="item.name"
  293. :value="item.id"
  294. />
  295. </el-select>
  296. </el-form-item>
  297. <el-form-item v-if="interviewNode.assigneeType === 'deptLeader'">
  298. <el-cascader
  299. v-model="assigneeDept"
  300. size="small"
  301. :options="deptList"
  302. :props="{multiple:true,checkStrictly:true,emitPath:false}"
  303. placeholder="请选择部门"
  304. filterable
  305. style="width: 400px;"
  306. />
  307. </el-form-item>
  308. </el-form>
  309. </el-carousel-item>
  310. </el-carousel>
  311. <template #footer>
  312. <el-button :disabled="!preValidate" @click="prevStep">上一步</el-button>
  313. <el-button v-if="step <= 2" type="primary" :disabled="!nextValidate" @click="nextStep">下一步</el-button>
  314. <el-button v-if="step >= 3" type="primary" :disabled="!publishValidate" @click.stop="onPublishSubmit">发布</el-button>
  315. </template>
  316. <TargetSearch
  317. :visible.sync="showTargetSearch"
  318. :selectedCheckList="okrs"
  319. :showSelectedData="selectedOkrs"
  320. @confirm="onOkrSelected"
  321. />
  322. <TemplateSelector
  323. :show-visible.sync="showTemplateSearch"
  324. :selected-template="templateSelected"
  325. @confirm="onTemplateConfirm"
  326. />
  327. <PerEmployeeSelector
  328. :show-visible.sync="showEmployeeSearch"
  329. :selected-employees="employeeSelected"
  330. :employee-status="1"
  331. @confirm="onEmployeeConfirm"
  332. />
  333. </el-dialog>
  334. </template>
  335. <script>
  336. import Template from "../../examine/components/Template.vue";
  337. import moment from "moment/moment";
  338. import SelectCircle from '@/newPerformance/components/TemplateDetails/SelectCircle';
  339. import TargetSearch from "@/performance/views/assessManagement/TargetSearch";
  340. import TemplateSelector from "./TemplateSelector.vue";
  341. import PerEmployeeSelector from "./PerEmployeeSelector.vue";
  342. export default {
  343. name: 'TemplateMixedPublish',
  344. components:{
  345. PerEmployeeSelector,
  346. TemplateSelector,
  347. TargetSearch,
  348. Template,
  349. SelectCircle,
  350. },
  351. props: {
  352. showVisible:{
  353. type:Boolean,
  354. default:false
  355. }
  356. },
  357. data() {
  358. return {
  359. userInfo: this.$userInfo(),
  360. innerVisible:this.showVisible,
  361. showTargetSearch:false,
  362. okrs:[],
  363. selectedOkrs:[],
  364. showTemplateSearch:false,
  365. showEmployeeSearch:false,
  366. currentTemplate:null,
  367. step:0,
  368. dateRange:[],
  369. year:'',
  370. month:'',
  371. publishData:{
  372. title:'',
  373. cycleType:'0',
  374. cateId:0,
  375. },
  376. dateOptions: [],
  377. dateParameter: {
  378. year: moment().format('YYYY'),
  379. cycle_type: 0,
  380. dateId: 1,
  381. name: '选择年度',
  382. },
  383. cateList:[
  384. { cateId: 0, name: '无分类' },
  385. ],
  386. templateList:[],
  387. loading: false,
  388. interviewNode:{
  389. id:'',
  390. type:'',
  391. enable:false,
  392. assigneeType:'',
  393. leaderLevel:1,
  394. assigneeIds:'',
  395. multipleType:'',
  396. allows:'',
  397. weight:'',
  398. children:[],
  399. },
  400. levelOptions: [
  401. {
  402. value: 1,
  403. label: '直接管理员'
  404. },
  405. {
  406. value: 2,
  407. label: '二级管理员'
  408. },
  409. {
  410. value: 3,
  411. label: '三级管理员'
  412. },
  413. {
  414. value: 4,
  415. label: '四级管理员'
  416. },
  417. {
  418. value: 5,
  419. label: '五级管理员'
  420. },
  421. {
  422. value: 6,
  423. label: '六级管理员'
  424. }
  425. ],
  426. employees: this.$getEmployeeMap(),
  427. postList: [],
  428. deptList: [],
  429. assigneeUsers:[],
  430. assigneePosts:[],
  431. assigneeDept:[],
  432. }
  433. },
  434. computed:{
  435. startDate(){
  436. switch (this.publishData.cycleType){
  437. case '0':
  438. return !this.dateRange || this.dateRange.length !== 2 ? '' : moment(this.dateRange[0]).format('YYYY-MM-DD');
  439. case '1':
  440. return !this.year ? '' : moment(`${this.year}-01-01`).startOf('year').format('YYYY-MM-DD');
  441. case '2':
  442. return !['上半年','下半年'].includes(this.dateParameter.name) ? '' : (this.dateParameter.name === '上半年' ? moment(this.dateParameter.year).startOf('year').format('YYYY-MM-DD') : moment(this.dateParameter.year).startOf('year').add(6,'months').format('YYYY-MM-DD'));
  443. case '3':
  444. if (!['第一季度','第二季度','第三季度','第四季度'].includes(this.dateParameter.name)) return '';
  445. switch (this.dateParameter.name){
  446. case '第一季度':
  447. return this.getQuarterDates(this.dateParameter.year,1).start.format('YYYY-MM-DD');
  448. case '第二季度':
  449. return this.getQuarterDates(this.dateParameter.year,2).start.format('YYYY-MM-DD');
  450. case '第三季度':
  451. return this.getQuarterDates(this.dateParameter.year,3).start.format('YYYY-MM-DD');
  452. case '第四季度':
  453. return this.getQuarterDates(this.dateParameter.year,4).start.format('YYYY-MM-DD');
  454. default:
  455. return '';
  456. }
  457. case '4':
  458. if (!this.month) return '';
  459. let [year,month] = this.month.split('-');
  460. if (!year || !month) return '';
  461. return moment(`${year}-${month}-01`).format('YYYY-MM-DD');
  462. default:
  463. return '';
  464. }
  465. },
  466. endDate(){
  467. switch (this.publishData.cycleType){
  468. case '0':
  469. return !this.dateRange || this.dateRange.length !== 2 ? '' : moment(this.dateRange[1]).format('YYYY-MM-DD');
  470. case '1':
  471. return !this.year ? '' : moment(`${this.year}-01-01`).endOf('year').format('YYYY-MM-DD');
  472. case '2':
  473. return !['上半年','下半年'].includes(this.dateParameter.name) ? '' : (this.dateParameter.name === '上半年' ? moment(this.dateParameter.year).startOf('year').add(6,'months').subtract(1,'days').format('YYYY-MM-DD') : moment(this.dateParameter.year).endOf('year').format('YYYY-MM-DD'));
  474. case '3':
  475. if (!['第一季度','第二季度','第三季度','第四季度'].includes(this.dateParameter.name)) return '';
  476. switch (this.dateParameter.name){
  477. case '第一季度':
  478. return this.getQuarterDates(this.dateParameter.year,1).end.format('YYYY-MM-DD');
  479. case '第二季度':
  480. return this.getQuarterDates(this.dateParameter.year,2).end.format('YYYY-MM-DD');
  481. case '第三季度':
  482. return this.getQuarterDates(this.dateParameter.year,3).end.format('YYYY-MM-DD');
  483. case '第四季度':
  484. return this.getQuarterDates(this.dateParameter.year,4).end.format('YYYY-MM-DD');
  485. default:
  486. return '';
  487. }
  488. case '4':
  489. if (!this.month) return '';
  490. let [year,month] = this.month.split('-');
  491. if (!year || !month) return '';
  492. return moment(`${year}-${month}-01`).endOf('month').format('YYYY-MM-DD');
  493. default:
  494. return '';
  495. }
  496. },
  497. templateMap(){
  498. let map = {}
  499. this.templateList.forEach(item => {
  500. map[item.templateId] = item;
  501. })
  502. return map;
  503. },
  504. templateSelected(){
  505. return this.templateList.map(item => item.templateId);
  506. },
  507. employeeSelected(){
  508. return !this.currentTemplate ? [] : this.currentTemplate.employees.map(item => item.employeeId);
  509. },
  510. preValidate(){
  511. if (this.loading) return false;
  512. switch (Number.parseInt(this.step)){
  513. case 0:
  514. return false;
  515. case 1:
  516. case 2:
  517. default:
  518. return true;
  519. }
  520. },
  521. nextValidate(){
  522. if (this.loading) return false;
  523. switch (Number.parseInt(this.step)){
  524. case 0:
  525. return this.baseValidate();
  526. case 1:
  527. return this.baseValidate() && this.okrValidate();
  528. case 2:
  529. return this.baseValidate() && this.okrValidate() && this.templateValidate();
  530. case 3:
  531. default:
  532. return false;
  533. }
  534. },
  535. publishValidate(){
  536. return !this.loading && this.baseValidate() && this.okrValidate() && this.templateValidate() && this.interviewValidate() ;
  537. },
  538. interviewFlow(){
  539. let assigneeMap = {
  540. leader: [],
  541. self: [],
  542. user: this.assigneeUsers,
  543. post: this.assigneePosts,
  544. deptLeader: this.assigneeDept
  545. }
  546. let node = {
  547. id:this.interviewNode.id,
  548. type:this.interviewNode.type,
  549. enable:this.interviewNode.enable,
  550. assigneeType:this.interviewNode.assigneeType,
  551. leaderLevel:this.interviewNode.leaderLevel,
  552. assigneeIds:assigneeMap[this.interviewNode.assigneeType] || [],
  553. multipleType:this.interviewNode.multipleType,
  554. allows:this.interviewNode.allows,
  555. weight:this.interviewNode.weight,
  556. children:this.interviewNode.children,
  557. };
  558. return {
  559. nodes:[node]
  560. }
  561. },
  562. },
  563. watch: {
  564. showVisible(val){
  565. this.innerVisible = val
  566. },
  567. 'interviewNode.assigneeType'(v){
  568. if (!v) return;
  569. switch (v){
  570. case 'self':
  571. this.interviewNode.leaderLevel = 1;
  572. this.assigneeUsers = [];
  573. this.assigneeDept = [];
  574. this.assigneePosts = [];
  575. break;
  576. case 'leader':
  577. this.assigneeUsers = [];
  578. this.assigneeDept = [];
  579. this.assigneePosts = [];
  580. break;
  581. case 'user':
  582. this.assigneeDept = [];
  583. this.assigneePosts = [];
  584. this.interviewNode.leaderLevel = 1;
  585. break;
  586. case 'post':
  587. this.assigneeUsers = [];
  588. this.assigneeDept = [];
  589. this.interviewNode.leaderLevel = 1;
  590. break;
  591. case 'deptLeader':
  592. this.assigneeUsers = [];
  593. this.assigneePosts = [];
  594. this.interviewNode.leaderLevel = 1;
  595. break;
  596. }
  597. }
  598. },
  599. methods: {
  600. matchErrMsg(err){
  601. return err.toString().replace(/[(Error: )]/g,'');
  602. },
  603. // 处理部门树状结构数据
  604. getTreeData(data) {
  605. for (var i = 0; i < data.length; i++) {
  606. data[i].checked = false;
  607. if (data[i].children.length < 1) {
  608. // children若为空数组,则将children设为undefined
  609. data[i].children = undefined;
  610. } else {
  611. // children若不为空数组,则继续 递归调用 本方法
  612. this.getTreeData(data[i].children);
  613. }
  614. }
  615. return data;
  616. },
  617. getQuarterDates(year, quarter) {
  618. // 计算季度的开始日期(第一个月的第一天)
  619. const startDate = moment().year(year).quarter(quarter).startOf('quarter');
  620. // 计算季度的结束日期(最后一个月的最后一天)
  621. const endDate = moment().year(year).quarter(quarter).endOf('quarter');
  622. return {
  623. start: startDate,
  624. end: endDate
  625. };
  626. },
  627. handleClose(){
  628. this.dataReset();
  629. this.$refs['carousel'].setActiveItem(this.step);
  630. this.$emit('update:showVisible',false);
  631. },
  632. dataReset(){
  633. this.step = 0;
  634. this.year = moment().format('YYYY');
  635. this.publishData = {
  636. title:'',
  637. cycleType:'0',
  638. cateId:0,
  639. templates:[],
  640. };
  641. this.okrs = [];
  642. this.selectedOkrs = [];
  643. this.templateList = [];
  644. this.dateRange = [];
  645. this.interviewNode = {
  646. id:`IT_${Date.now() + Math.floor(Math.random() * 10000)}`,
  647. type:'interview',
  648. enable:false,
  649. assigneeType:'self',
  650. leaderLevel:1,
  651. assigneeIds:[],
  652. multipleType:'or',
  653. allows:[],
  654. weight:0,
  655. children:[],
  656. };
  657. this.assigneeUsers = [];
  658. this.assigneePosts = [];
  659. this.assigneeDept = [];
  660. },
  661. initData(){
  662. this.dataReset();
  663. this.$nextTick(() => {
  664. this.$refs['carousel'].setActiveItem(this.step);
  665. });
  666. if (this.loading) return;
  667. this.loading = true;
  668. let result = true;
  669. Promise.all([
  670. this.$axiosUser('get',`/performance/cate/list/${this.userInfo.site_id}`),
  671. this.$axiosUser('get','/org/post'),
  672. this.$axiosUser('get', '/api/pro/department/tree', '', 'v2')
  673. ])
  674. .then(([cateRes,postRes,deptRes]) => {
  675. if (cateRes.data.code !== 1) throw new Error(cateRes.data.message);
  676. if (postRes.data.code !== 1) throw new Error(postRes.data.message);
  677. if (deptRes.data.code !== 1) throw new Error(deptRes.data.msg);
  678. this.cateList = [{cateId:0,name: '无分类'},...cateRes.data.data.list];
  679. this.postList = postRes.data.data;
  680. this.deptList = this.getTreeData(deptRes.data.data.list);
  681. })
  682. .catch(err => {
  683. this.$message.error(this.matchErrMsg(err));
  684. result = false;
  685. })
  686. .finally(() => {
  687. this.loading = false;
  688. if (!result) this.handleClose();
  689. })
  690. // this.getCateList();
  691. },
  692. prevStep(){
  693. if (this.step <= 0) return;
  694. this.step--;
  695. this.$refs['carousel'].prev();
  696. },
  697. nextStep(){
  698. if (this.step >= 3) return;
  699. this.step++;
  700. this.$refs['carousel'].next();
  701. },
  702. changeCycle(v){
  703. if (v === '2'){
  704. this.dateOptions = [
  705. { name: '上半年', id: 1, cycle_type: 3 },
  706. { name: '下半年', id: 2, cycle_type: 3 },
  707. ]
  708. } else if (v === '3'){
  709. this.dateOptions = [
  710. { name: '第一季度', id: 1, cycle_type: 2 },
  711. { name: '第二季度', id: 2, cycle_type: 2 },
  712. { name: '第三季度', id: 3, cycle_type: 2 },
  713. { name: '第四季度', id: 4, cycle_type: 2 }
  714. ]
  715. }
  716. },
  717. getCateList(){
  718. if (this.loading) return;
  719. this.loading = true;
  720. let url = `/performance/cate/list/${this.userInfo.site_id}`;
  721. this.$axiosUser('get', url, {})
  722. .then(res => {
  723. let { data: { code, data: { list } } } = res
  724. if (code === 1) this.cateList = [{cateId:0,name:'无分类'} ,...list]
  725. })
  726. .finally(() => {
  727. this.loading = false;
  728. });
  729. },
  730. dateConfirm(date) {
  731. let { year, name } = date;
  732. this.dateParameter.year = year;
  733. this.dateParameter.name = name;
  734. },
  735. onOkrSelected(okrs,data){
  736. this.okrs = okrs;
  737. this.selectedOkrs = data;
  738. },
  739. onTemplateConfirm(data){
  740. if (!data || data.length <= 0) {
  741. //清空模板
  742. this.templateList = [];
  743. return;
  744. }
  745. let dataMap = {}
  746. data.forEach(item => {
  747. dataMap[item.templateId] = item;
  748. });
  749. //移除模板
  750. this.templateList = this.templateList.filter(item => !!dataMap[item.templateId]);
  751. //添加模板
  752. data.forEach(item => {
  753. if (this.templateMap[item.templateId]) return;
  754. this.templateList.push({
  755. templateId:item.templateId,
  756. title:item.title,
  757. employees:[]
  758. })
  759. })
  760. },
  761. openEmployeeSelector(row){
  762. this.currentTemplate = row;
  763. this.showEmployeeSearch = true;
  764. },
  765. onEmployeeConfirm(data){
  766. if (!this.currentTemplate) return;
  767. if (!data || data.length <= 0) {
  768. //清空用户
  769. this.currentTemplate.employees = [];
  770. }
  771. let dataMap = {};
  772. data.forEach(item => {
  773. dataMap[item.employeeId] = item;
  774. });
  775. //移除用户
  776. this.currentTemplate.employees = this.currentTemplate.employees.filter(item => !!dataMap[item.employeeId]);
  777. //添加用户
  778. let employeeMap = {};
  779. this.currentTemplate.employees.forEach(item => {
  780. employeeMap[item.employeeId] = item;
  781. });
  782. data.forEach(item => {
  783. if (employeeMap[item.employeeId]) return;
  784. this.currentTemplate.employees.push({
  785. employeeId:item.employeeId,
  786. name:item.name
  787. });
  788. });
  789. this.currentTemplate = null;
  790. },
  791. baseValidate(){
  792. return this.publishData.title && this.publishData.title.length > 0 && this.publishData.title.length <= 20 && this.startDate && this.endDate;
  793. },
  794. okrValidate(){
  795. return true;
  796. },
  797. templateValidate(){
  798. return this.templateList && this.templateList.length > 0 && !this.templateList.some(template => {
  799. return !template.employees || template.employees.length <= 0
  800. });
  801. },
  802. interviewValidate(){
  803. let node = this.interviewFlow.nodes[0];
  804. return node.id && node.id.startsWith("IT_") && node.type === 'interview' && (!node.enable ? true : (node.assigneeType === 'leader' ? node.leaderLevel > 0 : (node.assigneeType === 'self' ? true : (!['post','user','deptLeader'].includes(node.assigneeType) ? false : (node.assigneeIds && node.assigneeIds.length > 0)))));
  805. },
  806. onPublishSubmit(){
  807. if (this.loading) return;
  808. this.loading = true;
  809. const params = {
  810. title: this.publishData.title,
  811. cateId: this.publishData.cateId,
  812. cycleType: this.publishData.cycleType,
  813. startDate: this.startDate,
  814. endDate: this.endDate,
  815. okrs: [...this.okrs],
  816. templates: this.templateList.map(template => {
  817. return {
  818. templateId: template.templateId,
  819. employees: template.employees.map(employee => {
  820. return {
  821. employeeId: employee.employeeId,
  822. ids: [],
  823. interviewFlow:this.interviewFlow
  824. }
  825. })
  826. }
  827. })
  828. }
  829. let url = `/performance/review/publish/templates/${this.userInfo.site_id}`;
  830. let result = true;
  831. this.$http.post(url,params)
  832. .then(res => {
  833. if (res.code !== 1) {
  834. this.$message.error(res.message || '非法请求');
  835. throw new Error(res.message || '非法请求');
  836. }
  837. this.$message.success("考核已发布");
  838. })
  839. .catch(err => {
  840. result = false;
  841. })
  842. .finally(() => {
  843. this.loading = false;
  844. if (result) this.handleClose();
  845. })
  846. },
  847. }
  848. }
  849. </script>
  850. <style scoped lang="scss">
  851. .selectBox{
  852. text-align: center;
  853. }
  854. /deep/ .card-cycle .el-card__body{
  855. padding: 20px 0;
  856. }
  857. </style>