PublishComp.vue 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  1. <template>
  2. <div style="width: 600px; margin: 10px auto; position: relative; ">
  3. <!-- <el-alert class="bounce animated" type="warning" :title="alertTilte" :closable="false" show-icon
  4. style="width: 100%; margin-top: 10px;"></el-alert> -->
  5. <div class="flex-box-ce" style="flex-direction: column; justify-content: center;">
  6. <!-- <div class="" style="font-size: 16px; font-weight: 600; text-align: center;">发起考核</div> -->
  7. <div style="margin: 0 auto 20px auto; font-size: 16px; font-weight: 600;">填写考核基本信息</div>
  8. <el-form :model="form" :rules="rules" ref="ruleForm" label-width="120px" size="small"
  9. label-position="right">
  10. <!-- <el-form-item label="考核标题" prop="title">
  11. <el-input v-model="form.title" style="width: 300px;"></el-input>
  12. </el-form-item> -->
  13. <el-form-item label="考核人员" prop="employeeIds">
  14. <el-select v-model="form.employeeIds" placeholder="请选择指定人员" multiple filterable
  15. style="width: 300px;" @change="changeEmployeeIds">
  16. <el-option v-for="item in employeeMap" :key="item.id" :label="item.name"
  17. :value="item.id"></el-option>
  18. </el-select>
  19. </el-form-item>
  20. <el-form-item label="周期">
  21. <el-radio-group v-model="form.cycleType" @change="changeCircle">
  22. <el-radio-button label="0">自定义</el-radio-button>
  23. <el-radio-button label="1">年度</el-radio-button>
  24. <el-radio-button label="2">半年度 </el-radio-button>
  25. <el-radio-button label="3">季度</el-radio-button>
  26. <el-radio-button label="4">月度</el-radio-button>
  27. </el-radio-group>
  28. </el-form-item>
  29. <!-- <el-form-item label="考核分类">
  30. <el-select v-model="form.cateId" @change="changeCateId" placeholder="请选择考核分类" style="width: 300px;">
  31. <el-option v-for="item in cateList" :key="item.cateId" :label="item.name" :value="item.cateId">
  32. </el-option>
  33. </el-select>
  34. </el-form-item> -->
  35. <el-form-item v-if="form.cycleType == 0" label="开始日期" prop="startDate">
  36. <el-date-picker v-model="form.startDate" type="date" value-format="yyyy-MM-dd" placeholder="选择开始日期"
  37. style="width: 300px;">
  38. </el-date-picker>
  39. </el-form-item>
  40. <el-form-item v-if="form.cycleType == 0" label="结束日期" prop="endDate">
  41. <el-date-picker v-model="form.endDate" type="date" value-format="yyyy-MM-dd" placeholder="选择结束日期"
  42. style="width: 300px;">
  43. </el-date-picker>
  44. </el-form-item>
  45. <el-form-item v-if="form.cycleType == 1" label="选择年份" prop="year">
  46. <el-date-picker v-model="form.year" type="year" value-format="yyyy" placeholder="选择年">
  47. </el-date-picker>
  48. </el-form-item>
  49. <el-form-item v-if="form.cycleType == 4" label="选择月份" prop="month">
  50. <el-date-picker v-model="form.month" type="month" placeholder="选择月" value-format="yyyy-MM">
  51. </el-date-picker>
  52. </el-form-item>
  53. <!-- <InterviewFlow form-label="主持人" dialog-title="面谈" node-type="interview" @onConfirm="finishHandle" /> -->
  54. <el-form-item label="启用面谈">
  55. <el-switch v-model="currentNode.enable"></el-switch>
  56. </el-form-item>
  57. <el-form-item label="主持人">
  58. <el-radio-group v-model="currentNode.assigneeType" :disabled="!currentNode.enable">
  59. <el-radio-button label="leader">管理员</el-radio-button>
  60. <el-radio-button label="user">指定人员</el-radio-button>
  61. <el-radio-button label="self">被考核人</el-radio-button>
  62. <el-radio-button label="post">岗位</el-radio-button>
  63. <el-radio-button label="deptLeader">部门</el-radio-button>
  64. </el-radio-group>
  65. </el-form-item>
  66. <el-form-item v-if="currentNode.assigneeType === 'leader'">
  67. <el-select v-model="selected_manager_ids" placeholder="请选择管理员" :disabled="!currentNode.enable"
  68. filterable style="width: 300px;" @change="changeManagerIds">
  69. <el-option v-for="item in levelOptions" :key="item.value" :label="item.label"
  70. :value="item.value" style="width: 300px;"></el-option>
  71. </el-select>
  72. </el-form-item>
  73. <el-form-item v-if="currentNode.assigneeType === 'user'">
  74. <el-select v-model="selected_employee_ids" placeholder="请选择指定人员" multiple
  75. :disabled="!currentNode.enable" filterable style="width: 300px;" @change="changeEmployeeIds">
  76. <el-option v-for="item in employeeMap" :key="item.id" :label="item.name" :value="item.id"
  77. style="width: 300px;"></el-option>
  78. </el-select>
  79. </el-form-item>
  80. <el-form-item v-if="currentNode.assigneeType === 'post'">
  81. <el-select v-model="selected_post_ids" placeholder="请选择岗位" multiple :disabled="!currentNode.enable"
  82. filterable style="width: 300px;" @change="changePostIds">
  83. <el-option v-for="item in postList" :key="item.id" :label="item.name" :value="item.id"
  84. style="width: 300px;"></el-option>
  85. </el-select>
  86. </el-form-item>
  87. <el-form-item v-if="currentNode.assigneeType === 'deptLeader'">
  88. <el-cascader ref="deptSelectRef" v-model="selected_dept_ids" size="small" style="width: 300px;"
  89. :options="deptList" :props="cascaderProps" placeholder="请选择部门" filterable clearable
  90. @change="deptChange" :disabled="!currentNode.enable"></el-cascader>
  91. </el-form-item>
  92. <!-- <el-form-item v-if="form.circle == 1" label="结束日期">
  93. <el-date-picker v-model="form.endDate" type="date" placeholder="选择结束日期" style="width: 300px;">
  94. </el-date-picker>
  95. </el-form-item> -->
  96. </el-form>
  97. <!-- <div style="margin: 20px auto;" v-if="selectedData && selectedData.length > 0">
  98. <div class="title">已选择的OKR</div>
  99. <div class="selected-item" v-for="item in selectedData" @click="changeSelectedOkrs">
  100. {{ item.name }}
  101. </div>
  102. </div>
  103. <el-link v-else type="primary" @click="isShowProject = true">请选择关联OKR</el-link> -->
  104. <div style="margin: 20px auto;">
  105. <el-button type="primary" @click="submitForm('ruleForm')" size="small">确定</el-button>
  106. <el-button @click="resetForm('ruleForm')" size="small">取 消</el-button>
  107. </div>
  108. </div>
  109. <el-drawer title="考核分类" :visible.sync="cateDetailsDialog" direction="rtl" :before-close="handleClose">
  110. <CateDetails v-if="cateDetailsDialog"></CateDetails>
  111. </el-drawer>
  112. <!-- 关联OKR -->
  113. <TargetSearch :visible.sync="isShowProject" @confirm="confirmProject" :selectedCheckList="okrs"
  114. :showSelectedData="selectedData"></TargetSearch>
  115. </div>
  116. </template>
  117. <script>
  118. import moment from 'moment'
  119. // 计算某一季度开始日期和结束日期
  120. function getQuarterDates(year, quarter) {
  121. // 计算季度的开始日期(第一个月的第一天)
  122. const startDate = moment().year(year).quarter(quarter).startOf('quarter');
  123. // 计算季度的结束日期(最后一个月的最后一天)
  124. const endDate = moment().year(year).quarter(quarter).endOf('quarter');
  125. return {
  126. start: startDate,
  127. end: endDate
  128. };
  129. }
  130. import { mapGetters } from 'vuex';
  131. import TargetSearch from '@/performance/views/assessManagement/TargetSearch'; // 对齐目标
  132. export default {
  133. components: {
  134. TargetSearch
  135. },
  136. model: {
  137. prop: 'showPublishDialog',
  138. event: 'close-dialog'
  139. },
  140. props: {
  141. showPublishDialog: {
  142. type: Boolean,
  143. default: false
  144. },
  145. templateIds: {
  146. type: Array,
  147. default: () => []
  148. },
  149. },
  150. data() {
  151. return {
  152. alertTilte: "如果有一个指标标题为空,将会发布不成功,请仔细检查数据!",
  153. employeeMap: this.$getEmployeeMap(), // 员工列表
  154. cateDetailsDialog: false, // 考核分类弹框
  155. form: {
  156. title: "",
  157. employeeIds: [],
  158. startDate: "",
  159. endDate: "",
  160. cycleType: "0", // 周期类型 0-未定义 1-年度 2-半年度 3-季度 4-月度
  161. year: "",
  162. cateId: 0
  163. },
  164. isShowProject: false,
  165. rules: {
  166. title: [
  167. { required: true, message: '请输入考核标题', trigger: 'blur' },
  168. ],
  169. employeeIds: [
  170. { required: true, message: '请选择考核人员', trigger: 'change' }
  171. ],
  172. startDate: [
  173. { required: true, message: '请选择开始日期', trigger: 'change' }
  174. ],
  175. endDate: [
  176. { required: true, message: '请选择结束日期', trigger: 'change' }
  177. ],
  178. year: [
  179. { required: true, message: '请选择年份', trigger: 'change' }
  180. ],
  181. month: [
  182. { required: true, message: '请选择月份', trigger: 'change' }
  183. ],
  184. },
  185. dateOptions: [],
  186. dateParameter: {
  187. year: moment().format('YYYY'),
  188. cycle_type: 0,
  189. dateId: 1,
  190. name: '选择年度',
  191. },
  192. cateList: [], // 考核分类列表,
  193. interview: false,
  194. params: null,
  195. okrs: [], // 关联的OKRS,
  196. selectedData: [],
  197. cascaderProps: {
  198. multiple: true, // 启用多选
  199. checkStrictly: true, // 父子节点不联动
  200. emitPath: false, // 选中值只返回当前节点的值
  201. },
  202. levelOptions: [
  203. {
  204. value: 1,
  205. label: '直接管理员'
  206. },
  207. {
  208. value: 2,
  209. label: '二级管理员'
  210. },
  211. {
  212. value: 3,
  213. label: '三级管理员'
  214. },
  215. {
  216. value: 4,
  217. label: '四级管理员'
  218. },
  219. {
  220. value: 5,
  221. label: '五级管理员'
  222. },
  223. {
  224. value: 6,
  225. label: '六级管理员'
  226. }
  227. ],
  228. deptList: JSON.parse(localStorage.getItem("deptList")), // 部门列表 - 树形结构
  229. dept_list: JSON.parse(localStorage.getItem("dept_list")), // 部门列表
  230. employeeMap: this.$getEmployeeMap(), // 员工列表
  231. postList: JSON.parse(localStorage.getItem("postList")), // 岗位列表
  232. selected_dept_ids: [], // 选择的部门列表
  233. selected_manager_ids: '', // 选择的管理员列表
  234. selected_post_ids: [], // 选择的岗位列表
  235. selected_employee_ids: [], // 选择的员工列表
  236. currentNode: {
  237. id: "IT_" + Date.now() + Math.floor(Math.random() * 10000),
  238. type: "interview",
  239. allows: [],
  240. enable: true,
  241. weight: 0,
  242. children: [],
  243. assigneeIds: [],
  244. leaderLevel: 1,
  245. assigneeType: "self",
  246. multipleType: "or"
  247. }
  248. }
  249. },
  250. computed: {
  251. ...mapGetters(['user_info'])
  252. },
  253. watch: {
  254. showPublish(v) {
  255. // if (v) this.getCateList()
  256. }
  257. },
  258. mounted() {
  259. // this.getCateList()
  260. },
  261. methods: {
  262. // 考核分类列表
  263. getCateList() {
  264. let url = `/performance/cate/list/${this.user_info.site_id}`;
  265. // this.loading = true
  266. this.$axiosUser('get', url, {}).then(res => {
  267. let { data: { code, data: { list, total } } } = res
  268. if (code == 1) {
  269. this.cateList = list
  270. this.form.cateId = this.cateList[0].cateId || 0
  271. }
  272. })
  273. },
  274. // 选择考核分类
  275. changeCateId(v) {
  276. console.log(v)
  277. },
  278. // 选择员工
  279. changeEmployeeIds(v) {
  280. if (v && v.length > 0) {
  281. v.forEach(item => {
  282. this.form.employeeIds.push(item)
  283. })
  284. }
  285. this.form.employeeIds = Array.from(new Set(this.form.employeeIds)); // 去重
  286. },
  287. changeCircle(v) {
  288. if (v == 2) this.dateOptions = [
  289. { name: '上半年', id: 1, cycle_type: 3 },
  290. { name: '下半年', id: 2, cycle_type: 3 },
  291. ]
  292. if (v == 3) this.dateOptions = [
  293. { name: '第一季度', id: 1, cycle_type: 2 },
  294. { name: '第二季度', id: 2, cycle_type: 2 },
  295. { name: '第三季度', id: 3, cycle_type: 2 },
  296. { name: '第四季度', id: 4, cycle_type: 2 }
  297. ]
  298. },
  299. dateConfirm(date) {
  300. let { year, name } = date;
  301. this.dateParameter.year = year;
  302. this.dateParameter.name = name;
  303. // 上半年度
  304. if (name === "上半年") {
  305. let startOfYear = moment().year(year).startOf('year'); // 获取年初
  306. let endOfFirstHalfYear = moment().year(year).startOf('year').add(6, 'months').subtract(1, 'days'); // 获取上半年结束日(6月30日)
  307. this.form.startDate = startOfYear.format('YYYY-MM-DD');
  308. this.form.endDate = endOfFirstHalfYear.format('YYYY-MM-DD');
  309. }
  310. // 下半年度
  311. if (name === "下半年") {
  312. let startOfSecondHalfYear = moment().year(year).startOf('year').add(6, 'months'); // 获取下半年开始日(7月1日)
  313. let endOfYear = moment().year(year).endOf('year'); // 获取年末
  314. this.form.startDate = startOfSecondHalfYear.format('YYYY-MM-DD');
  315. this.form.endDate = endOfYear.format('YYYY-MM-DD');
  316. }
  317. // 第一季度
  318. if (name === "第一季度") {
  319. const dates = getQuarterDates(year, 1);
  320. this.form.startDate = dates.start.format('YYYY-MM-DD');
  321. this.form.endDate = dates.end.format('YYYY-MM-DD');
  322. }
  323. // 第一季度
  324. if (name === "第二季度") {
  325. const dates = getQuarterDates(year, 2);
  326. this.form.startDate = dates.start.format('YYYY-MM-DD');
  327. this.form.endDate = dates.end.format('YYYY-MM-DD');
  328. }
  329. // 第三季度
  330. if (name === "第三季度") {
  331. const dates = getQuarterDates(year, 3);
  332. this.form.startDate = dates.start.format('YYYY-MM-DD');
  333. this.form.endDate = dates.end.format('YYYY-MM-DD');
  334. }
  335. // 第四季度
  336. if (name === "第四季度") {
  337. const dates = getQuarterDates(year, 4);
  338. this.form.startDate = dates.start.format('YYYY-MM-DD');
  339. this.form.endDate = dates.end.format('YYYY-MM-DD');
  340. }
  341. },
  342. handleClose() {
  343. this.cateDetailsDialog = false
  344. },
  345. // 关闭弹窗
  346. handleCloseDialog() {
  347. this.$emit('close-dialog', false);
  348. },
  349. // 选择管理员
  350. changeManagerIds(v) {
  351. this.currentNode.leaderLevel = v
  352. },
  353. // 选择员工
  354. changeEmployeeIds(v) {
  355. if (v && v.length > 0) {
  356. this.currentNode.assigneeIds = v
  357. }
  358. },
  359. // 选择岗位
  360. changePostIds(v) {
  361. if (v && v.length > 0) {
  362. this.currentNode.assigneeIds = v
  363. }
  364. },
  365. // 选择部门
  366. deptChange(val) {
  367. if (val && val.length > 0) {
  368. // 获取当前选中的节点
  369. const checkedNodes = this.$refs.deptSelectRef.getCheckedNodes();
  370. this.selected_dept_ids = checkedNodes.map((node) => node.value);
  371. this.currentNode.assigneeIds = this.selected_dept_ids
  372. this.currentNode.assigneeIds = Array.from(new Set(this.currentNode.assigneeIds)); // 去重
  373. }
  374. },
  375. checkInterviewNode() {
  376. // 验证表单数据
  377. this.currentNode.assigneeIds = []
  378. // 回显选中的管理者
  379. if (this.currentNode.assigneeType == 'leader') {
  380. if (this.selected_manager_ids) this.currentNode.leaderLevel = this.selected_manager_ids
  381. else return this.$message.error("请选择管理员")
  382. }
  383. // 回显选中的被考核人
  384. if (this.currentNode.assigneeType == 'self') {
  385. this.currentNode.assigneeIds.push(this.user_info.id);
  386. }
  387. // 回显选中的岗位
  388. if (this.currentNode.assigneeType == 'post') {
  389. if (this.selected_post_ids && this.selected_post_ids.length > 0)
  390. this.selected_post_ids.forEach(postId => {
  391. this.currentNode.assigneeIds.push(postId);
  392. })
  393. else return this.$message.error("请选择岗位")
  394. }
  395. // 回显选中的指定人员
  396. if (this.currentNode.assigneeType == 'user') {
  397. if (this.selected_employee_ids && this.selected_employee_ids.length > 0)
  398. this.selected_employee_ids.forEach(employeeId => {
  399. this.currentNode.assigneeIds.push(employeeId);
  400. })
  401. else return this.$message.error("请选择指定人员")
  402. }
  403. // 回显选中的部门列表
  404. if (this.currentNode.assigneeType == 'deptLeader') {
  405. if (this.selected_dept_ids && this.selected_dept_ids.length > 0)
  406. this.selected_dept_ids.forEach(dept_id => {
  407. this.currentNode.assigneeIds.push(dept_id);
  408. })
  409. else return this.$message.error("请选择部门")
  410. }
  411. // 去重
  412. this.currentNode.assigneeIds = Array.from(new Set(this.currentNode.assigneeIds))
  413. // let nodes = [this.currentNode];
  414. console.log(this.currentNode)
  415. },
  416. // 确定按钮
  417. submitForm(formName) {
  418. this.$refs[formName].validate((valid) => {
  419. if (valid) {
  420. // 周期类型 0 - 未定义 1 - 年度 2 - 半年度 3 - 季度 4 - 月度
  421. // 年度
  422. if (this.form.cycleType == 1) {
  423. let { year } = this.form;
  424. // 计算年份的开始时间(即1月1日)
  425. const startOfYear = moment().year(year).startOf('year');
  426. this.form.startDate = startOfYear.format('YYYY-MM-DD');
  427. // 计算年份的结束时间(即12月31日)
  428. const endOfYear = moment().year(year).endOf('year');
  429. // 年度结束时间
  430. this.form.endDate = endOfYear.format('YYYY-MM-DD');
  431. }
  432. // 月
  433. if (this.form.cycleType == 4) {
  434. let [year, month] = this.form.month.split("-");
  435. // 计算开始时间(该月的第1天)
  436. const startOfMonth = moment(`${year}-${month}-01`);
  437. this.form.startDate = startOfMonth.format('YYYY-MM-DD');
  438. // 计算结束时间(该月的最后一天)
  439. const endOfMonth = moment(startOfMonth).endOf('month');
  440. this.form.endDate = endOfMonth.format('YYYY-MM-DD');
  441. }
  442. this.params = {
  443. templateIds: this.templateIds,
  444. ...this.form,
  445. okrs: this.okrs
  446. }
  447. this.checkInterviewNode()
  448. this.params = {
  449. ...this.params,
  450. interviewFlow: {
  451. nodes: [...this.currentNode]
  452. }
  453. },
  454. // this.interview = true
  455. this.$emit('onConfirm', this.params);
  456. // this.handleCloseDialog();
  457. } else {
  458. console.log('error submit!!');
  459. return false;
  460. }
  461. });
  462. },
  463. // 重置表单
  464. resetForm(formName) {
  465. this.$refs[formName].resetFields();
  466. this.handleCloseDialog();
  467. },
  468. finishHandle(nodes) {
  469. this.params = {
  470. ...this.params,
  471. interviewFlow: {
  472. nodes
  473. }
  474. },
  475. this.$emit('onConfirm', this.params, this.selectedData);
  476. this.handleCloseDialog();
  477. },
  478. changeSelectedOkrs() {
  479. this.isShowProject = true
  480. },
  481. confirmProject(okrs, selectedData) {
  482. this.okrs = okrs;
  483. this.selectedData = selectedData;
  484. }
  485. }
  486. }
  487. </script>
  488. <style scoped="scoped" lang="scss">
  489. .selectBox {
  490. width: 300px;
  491. height: 40px;
  492. border-radius: 40px;
  493. border: 1px solid #eee;
  494. display: flex;
  495. align-items: center;
  496. justify-content: center;
  497. margin: 0 auto;
  498. }
  499. .status-btn-box {
  500. width: 120px;
  501. height: 40px;
  502. z-index: 10;
  503. position: absolute;
  504. top: 20px;
  505. left: 20px;
  506. .status-btn {
  507. width: 100%;
  508. height: 100%;
  509. background: transparent;
  510. border: 2px dashed;
  511. text-align: center;
  512. font-size: 20px;
  513. line-height: 40px;
  514. }
  515. }
  516. .title {
  517. margin-bottom: 5px;
  518. }
  519. .selected-item {
  520. padding: 5px;
  521. box-sizing: border-box;
  522. border-radius: 4px;
  523. background: #f7f7f7;
  524. margin-bottom: 5px;
  525. &:hover {
  526. cursor: pointer;
  527. }
  528. }
  529. </style>