DepartProcessAdd.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. <template>
  2. <div style="background-color: white;">
  3. <!-- 加上load效果 -->
  4. <div class="form_center" v-loading="loading">
  5. <el-form :model="form" :rules="rules" ref="form" label-width="120px">
  6. <el-row :gutter="20">
  7. <el-col :span="8">
  8. <el-form-item label="工单名称" prop="name">
  9. <el-input v-model="form.name"></el-input>
  10. </el-form-item>
  11. </el-col>
  12. <el-col :span="8">
  13. <el-form-item label="工单开始时间" prop="startTime">
  14. <el-date-picker v-model="form.startTime" type="datetime" placeholder="选择日期时间"
  15. format="yyyy-MM-dd HH:mm" value-format="yyyy-MM-dd HH:mm">
  16. </el-date-picker>
  17. </el-form-item>
  18. </el-col>
  19. <el-col :span="8">
  20. <el-form-item label="所属团组" prop="groupId">
  21. <el-select filterable v-model="form.groupId" placeholder="请选择">
  22. <el-option v-for="item in initResp.groupList" :key="item.id" :label="item.teamName"
  23. :value="item.id"></el-option>
  24. </el-select>
  25. </el-form-item>
  26. </el-col>
  27. </el-row>
  28. <el-row :gutter="20">
  29. <el-col :span="8">
  30. <!-- 添加筛选 -->
  31. <el-form-item label="指派用户" prop="assignedUserId">
  32. <el-select @change="userChange" filterable v-model="form.assignedUserId" placeholder="请选择">
  33. <el-option v-for="item in initResp.users" :key="item.id" :label="item.cnName"
  34. :value="item.id"></el-option>
  35. </el-select>
  36. </el-form-item>
  37. </el-col>
  38. <el-col :span="8">
  39. <el-form-item label="外办选项" prop="foreignOptionId">
  40. <el-select v-model="form.foreignOptionId" placeholder="请选择">
  41. <el-option v-for="item in initResp.foreignLv" :key="item.id" :label="item.name"
  42. :value="item.id"></el-option>
  43. </el-select>
  44. </el-form-item>
  45. </el-col>
  46. <el-col :span="8">
  47. </el-col>
  48. </el-row>
  49. <div class="form_detail">
  50. <el-collapse v-model="activeNames">
  51. <el-collapse-item title="主要任务" name="1">
  52. <el-table :data="mainTasks" style="width: 100%" border>
  53. <el-table-column type="index" label="序号" width="80"></el-table-column>
  54. <el-table-column prop="name" label="单项任务名称" width="300">
  55. <template slot-scope="scope">
  56. <el-input :disabled="scope.$index + 1 <= form.action - 1" height="70"
  57. type="textarea" v-model="scope.row.name" size="mini"></el-input>
  58. </template>
  59. </el-table-column>
  60. <el-table-column prop="priorityId" label="优先级" width="100">
  61. <template slot-scope="scope">
  62. <el-select :disabled="scope.$index + 1 <= form.action - 1"
  63. v-model="scope.row.priorityId" size="mini" placeholder="请选择">
  64. <el-option v-for="item in initResp.taskLv" :key="item.id" :label="item.name"
  65. :value="item.id"></el-option>
  66. </el-select>
  67. </template>
  68. </el-table-column>
  69. <el-table-column prop="isUrgent" label="是否加急" width="100">
  70. <template slot-scope="scope">
  71. <el-switch :disabled="scope.$index + 1 <= form.action - 1"
  72. v-model="scope.row.isUrgent" active-color="#13ce66"
  73. inactive-color="#ff4949">
  74. </el-switch>
  75. </template>
  76. </el-table-column>
  77. <el-table-column prop="assignedUserId" label="指派给谁" width="120">
  78. <template slot-scope="scope">
  79. <el-select :disabled="scope.$index + 1 <= form.action - 1"
  80. v-model="scope.row.assignedUserId" size="mini" placeholder="请选择">
  81. <el-option v-for="item in initResp.users" :key="item.id"
  82. :label="item.cnName" :value="item.id"></el-option>
  83. </el-select>
  84. </template>
  85. </el-table-column>
  86. <el-table-column prop="timeRange" label="任务时间(起止)">
  87. <template slot-scope="scope">
  88. <el-date-picker :disabled="scope.$index + 1 <= form.action - 1"
  89. v-model="scope.row.timeRange" type="datetimerange" range-separator="至"
  90. start-placeholder="开始日期" end-placeholder="结束日期" size="mini"
  91. format="yyyy-MM-dd HH:mm:ss" value-format="yyyy-MM-dd HH:mm:ss"
  92. @change="onTimeRangeChange(scope.$index)">
  93. </el-date-picker>
  94. </template>
  95. </el-table-column>
  96. <el-table-column prop="durationHours" label="任务默认完成小时数" width="160">
  97. <template slot-scope="scope">
  98. <el-input-number :disabled="scope.$index + 1 <= form.action - 1"
  99. v-model="scope.row.durationHours" size="mini" :min="0"
  100. :step="1"></el-input-number>
  101. </template>
  102. </el-table-column>
  103. <!-- <el-table-column prop="requiredFieldsValue" label="必填项设置" width="120">
  104. <template slot-scope="scope">
  105. <el-checkbox-group v-model="scope.row.requiredFieldsValue"
  106. @change="handleCheckedCitiesChange">
  107. <div v-for="requiredFieldsItem in requiredFieldsItems">
  108. <el-checkbox :label="requiredFieldsItem.id"
  109. :key="requiredFieldsItem.id">{{
  110. requiredFieldsItem.name
  111. }}
  112. </el-checkbox>
  113. </div>
  114. </el-checkbox-group>
  115. </template>
  116. </el-table-column> -->
  117. <el-table-column label="操作" width="120">
  118. <template slot-scope="scope">
  119. <div v-if="!(scope.$index + 1 <= form.action - 1)">
  120. <el-button class="czbtn" size="mini" type="primary"
  121. @click="insertTaskAbove(scope.$index)">插入行↑</el-button>
  122. <br />
  123. <!-- 删除图标加在文字后方 -->
  124. <el-button class="czbtn" style="margin: 10px 0;" size="mini" type="danger"
  125. @click="deleteTask(scope.$index)">删除
  126. <i class="el-icon-delete"></i></el-button>
  127. <br />
  128. <el-button class="czbtn" size="mini" type="primary"
  129. @click="insertTaskBelow(scope.$index)">插入行↓</el-button>
  130. </div>
  131. </template>
  132. </el-table-column>
  133. </el-table>
  134. <div style="margin-top: 10px;text-align: center;">
  135. <el-button size="small" type="primary" @click="addTask">新增任务</el-button>
  136. </div>
  137. </el-collapse-item>
  138. <el-collapse-item title="临时额外任务" name="2">
  139. <div>临时额外任务内容区域</div>
  140. </el-collapse-item>
  141. </el-collapse>
  142. </div>
  143. <br />
  144. <div style="text-align: right;">
  145. <el-form-item>
  146. <el-button type="primary" @click="submitForm">提交</el-button>
  147. <el-button @click="resetForm">返回</el-button>
  148. </el-form-item>
  149. </div>
  150. </el-form>
  151. </div>
  152. </div>
  153. </template>
  154. <script>
  155. export default {
  156. data() {
  157. return {
  158. token: '',
  159. userId: '',
  160. typeId: 1453,
  161. activeNames: ['1'], // 只展开主要任务
  162. mainTasks: [
  163. ],
  164. form: {
  165. id: 0,
  166. name: '',
  167. startTime: '',
  168. groupId: '',
  169. assignedUserId: '',
  170. foreignOptionId: '',
  171. action: 1
  172. },
  173. rules: {
  174. name: [
  175. { required: true, message: '请输入工单名称', trigger: 'blur' }
  176. ],
  177. startTime: [
  178. { required: true, message: '请选择工单开始时间', trigger: 'change' }
  179. ],
  180. groupId: [
  181. { required: true, message: '请输入所属团组ID', trigger: 'blur' }
  182. ],
  183. assignedUserId: [
  184. { required: true, message: '请输入指派用户ID', trigger: 'blur' }
  185. ],
  186. foreignOptionId: [
  187. { required: true, message: '请选择外办选项ID', trigger: 'change' }
  188. ]
  189. },
  190. requiredFieldsItems: [
  191. { name: "文本描述", id: 1 },
  192. { name: "相关文件", id: 2 }
  193. ],
  194. initResp: {
  195. groupList: [],
  196. taskLv: [],
  197. foreignLv: [],
  198. users: [],
  199. },
  200. loading: true,
  201. }
  202. },
  203. mounted() {
  204. this.token = JSON.parse(localStorage.getItem('userinif')).token;
  205. this.userId = JSON.parse(localStorage.getItem('userinif')).userInfo.userId;
  206. //获取$router中的query
  207. this.form.id = this.$route.query.id;
  208. if (this.$route.query.edit) {
  209. this.getDetail();
  210. } else {
  211. this.init();
  212. }
  213. },
  214. methods: {
  215. // 计算两个时间之间的小时数
  216. calculateHours(startTime, endTime) {
  217. if (!startTime || !endTime) return 0;
  218. const start = new Date(startTime);
  219. const end = new Date(endTime);
  220. // 计算时间差(毫秒)
  221. const diff = end - start;
  222. // 转换为小时数
  223. const hours = diff / (1000 * 60 * 60);
  224. // 保留一位小数
  225. return Math.round(hours * 10) / 10;
  226. },
  227. // 当时间范围改变时更新默认小时数
  228. onTimeRangeChange(index) {
  229. const task = this.mainTasks[index];
  230. if (task.timeRange && task.timeRange.length === 2) {
  231. task.durationHours = this.calcWorkHoursExact(task.timeRange[0], task.timeRange[1]);
  232. } else {
  233. task.durationHours = 0;
  234. }
  235. },
  236. addTask() {
  237. this.mainTasks.push({
  238. "id": 0,
  239. "name": "",
  240. "priorityId": 1456,
  241. "isUrgent": false,
  242. "assignedUserId": this.form.assignedUserId || '',
  243. "durationHours": 0,
  244. "workOrderId": 0,
  245. "sort": this.mainTasks.length + 1,
  246. "timeRange": [],
  247. "requiredFieldsValue": []
  248. });
  249. },
  250. insertTaskAbove(index) {
  251. // 在指定位置上方插入新行
  252. this.mainTasks.splice(index, 0, {
  253. "id": 0,
  254. "name": "",
  255. "priorityId": 1456,
  256. "isUrgent": false,
  257. "assignedUserId": this.form.assignedUserId || '',
  258. "durationHours": 0,
  259. "workOrderId": 0,
  260. "sort": this.mainTasks.length + 1,
  261. "timeRange": [],
  262. "requiredFieldsValue": []
  263. });
  264. },
  265. insertTaskBelow(index) {
  266. // 在指定位置下方插入新行
  267. this.mainTasks.splice(index + 1, 0, {
  268. "id": 0,
  269. "name": "",
  270. "priorityId": 1456,
  271. "isUrgent": false,
  272. "assignedUserId": this.form.assignedUserId || '',
  273. "durationHours": 0,
  274. "workOrderId": 0,
  275. "sort": this.mainTasks.length + 1,
  276. "timeRange": [],
  277. "requiredFieldsValue": []
  278. });
  279. },
  280. deleteTask(index) {
  281. this.mainTasks.splice(index, 1);
  282. },
  283. submitForm() {
  284. this.$refs.form.validate((valid) => {
  285. if (valid) {
  286. this.save();
  287. } else {
  288. // 表单验证失败
  289. this.$message({
  290. message: '请检查填写内容',
  291. type: 'warning'
  292. });
  293. return false;
  294. }
  295. });
  296. },
  297. resetForm() {
  298. this.$refs.form.resetFields();
  299. this.$router.back();
  300. },
  301. /**
  302. * 计算两个时间之间的工作小时数
  303. * 工作时间:周一至周五,上午9:00-12:00,下午13:30-18:00
  304. * @param {string} startStr "yyyy-mm-dd HH:mm"
  305. * @param {string} endStr "yyyy-mm-dd HH:mm"
  306. * @returns {number} 工作小时数
  307. */
  308. calcWorkHoursExact(startStr, endStr) {
  309. console.log(startStr, endStr);
  310. const start = new Date(startStr);
  311. const end = new Date(endStr);
  312. if (end <= start) return 0;
  313. const workPeriods = [
  314. { start: 9 * 60, end: 12 * 60 }, // 上午 9:00-12:00
  315. { start: 13.5 * 60, end: 18 * 60 } // 下午 13:30-18:00
  316. ];
  317. let totalMinutes = 0;
  318. let current = new Date(start);
  319. while (current < end) {
  320. const day = current.getDay();
  321. if (day >= 1 && day <= 5) { // 工作日
  322. for (const period of workPeriods) {
  323. const periodStart = new Date(current);
  324. periodStart.setHours(Math.floor(period.start / 60), period.start % 60, 0, 0);
  325. const periodEnd = new Date(current);
  326. periodEnd.setHours(Math.floor(period.end / 60), period.end % 60, 0, 0);
  327. // 每天的有效区间
  328. const effectiveStart = periodStart > current ? periodStart : current;
  329. const effectiveEnd = periodEnd < end ? periodEnd : end;
  330. if (effectiveEnd > effectiveStart) {
  331. totalMinutes += (effectiveEnd - effectiveStart) / 60000;
  332. }
  333. }
  334. }
  335. // 下一天
  336. current.setDate(current.getDate() + 1);
  337. current.setHours(0, 0, 0, 0);
  338. }
  339. return (totalMinutes / 60).toFixed(2);
  340. },
  341. init() {
  342. this.$axios({
  343. method: 'post',
  344. url: '/api/Task/TaskInit',
  345. data: {
  346. typeId: 1453,
  347. }
  348. }).then(response => {
  349. // 处理响应数据
  350. if (response.data.code == 200) {
  351. this.initResp = response.data.data;
  352. var maintasks = response.data.data.defaultTask;
  353. maintasks.forEach(item => {
  354. item.timeRange = [item.startTime, item.endTime];
  355. item.requiredFieldsValue = [];
  356. })
  357. this.mainTasks = maintasks;
  358. this.form.startTime = response.data.data.startTime;
  359. console.log(this.mainTasks);
  360. } else {
  361. this.$message({
  362. message: response.data.msg,
  363. type: 'warning'
  364. });
  365. }
  366. }).catch(error => {
  367. // 处理错误
  368. console.error(error);
  369. }).finally(() => {
  370. this.loading = false;
  371. });
  372. },
  373. handleCheckedCitiesChange(val) {
  374. console.log(val);
  375. },
  376. save() {
  377. var tasks = this.mainTasks;
  378. var i = 1;
  379. // 将forEach改为for循环
  380. for (let j = 0; j < tasks.length; j++) {
  381. const item = tasks[j];
  382. item.startTime = item.timeRange[0];
  383. item.endTime = item.timeRange[1];
  384. item.sort = i;
  385. i = i + 1;
  386. if (!item.name) {
  387. this.$message({
  388. message: '请填写任务名称',
  389. type: 'warning'
  390. });
  391. return false;
  392. }
  393. if (!item.assignedUserId) {
  394. this.$message({
  395. message: '请选择任务负责人',
  396. type: 'warning'
  397. });
  398. return false;
  399. }
  400. if (!item.timeRange[0] || !item.timeRange[1]) {
  401. this.$message({
  402. message: '请选择任务时间',
  403. type: 'warning'
  404. });
  405. return false;
  406. }
  407. }
  408. var form = this.form;
  409. form.createUserId = this.userId;
  410. form.typeId = this.typeId;
  411. form.tasks = tasks;
  412. this.$axios({
  413. method: 'post',
  414. url: '/api/Task/TaskOperation',
  415. data: form
  416. }).then(response => {
  417. // 处理响应数据
  418. if (response.data.code == 200) {
  419. this.$message({
  420. message: '保存成功',
  421. type: 'success'
  422. });
  423. this.$router.back();
  424. } else {
  425. this.$message({
  426. message: response.data.msg,
  427. type: 'warning'
  428. });
  429. }
  430. }).catch(error => {
  431. // 处理错误
  432. console.error(error);
  433. });
  434. },
  435. userChange() {
  436. this.mainTasks.forEach(item => {
  437. item.assignedUserId = this.form.assignedUserId;
  438. })
  439. },
  440. getDetail() {
  441. this.$axios({
  442. method: 'post',
  443. url: '/api/Task/TaskDetail',
  444. data: {
  445. id: this.form.id
  446. }
  447. }).then(response => {
  448. // 处理响应数据
  449. if (response.data.code == 200) {
  450. this.initResp = response.data.data;
  451. var maintasks = response.data.data.workTask;
  452. maintasks.forEach(item => {
  453. item.timeRange = [item.startTime, item.endTime];
  454. item.requiredFieldsValue = [];
  455. })
  456. this.mainTasks = maintasks;
  457. this.form = response.data.data.workOrder;
  458. } else {
  459. this.$message({
  460. message: response.data.msg,
  461. type: 'warning'
  462. });
  463. }
  464. }).catch(error => {
  465. // 处理错误
  466. console.error(error);
  467. }).finally(() => {
  468. this.loading = false;
  469. });
  470. },
  471. }
  472. }
  473. </script>
  474. <style scoped>
  475. .form_center {
  476. max-width: 100%;
  477. /* 限制最大宽度 */
  478. width: 100%;
  479. margin: 20px auto;
  480. padding: 20px;
  481. box-sizing: border-box;
  482. }
  483. </style>