bonusPointsPopup.vue 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969
  1. <template>
  2. <div>
  3. <!-- 奖扣ab分弹窗 -->
  4. <el-dialog :title="title" :visible.sync="visible" top="3%" :close-on-click-modal="false" :before-close="closePopup" width="600px">
  5. <div>
  6. <el-form :model="dialogData" ref="dialogData" label-width="80px" v-loading="loading">
  7. <el-form-item label="奖扣对象" prop="members" :rules="[{ required: true, message: '请选择奖扣对象', trigger: 'change' }]">
  8. <el-row>
  9. <el-col :span="18">
  10. <el-input auto-complete="off" v-model="employeeName" placeholder="请选择奖扣对象"></el-input>
  11. <div @click="noPersonnelListTips" style=" position: absolute; top: 0; right: 0; left: 0; bottom: 0; z-index: 9;"></div>
  12. <slot></slot>
  13. </el-col>
  14. </el-row>
  15. </el-form-item>
  16. <div v-for="(item, index) in dialogData.items" :key="index" @click.stop="setIndex(index)">
  17. <div style="overflow: hidden;">
  18. <span style="line-height: 36px;">录入明细({{ index + 1 }})</span>
  19. <el-button type="text" class="fr" v-show="index > 0 || dialogData.items.length > 1" @click="delItem(index)">删除</el-button>
  20. </div>
  21. <el-form-item label="指定规则">
  22. <el-switch :disabled="ruleOnoff" @change="switchChange(index, item.rule_switch)" v-model="item.rule_switch"></el-switch>
  23. </el-form-item>
  24. <el-form-item
  25. class="test_cascader_id"
  26. label="选择分类"
  27. v-if="!item.rule_switch"
  28. :prop="'items.' + index + '.rule_list_value'"
  29. :rules="[{ required: true, message: '请选择规则分类', trigger: 'blur' }]"
  30. >
  31. <el-cascader
  32. v-model="item.rule_list_value"
  33. :ref="'ruleCascader'+index"
  34. :popper-class="'ruleClass'"
  35. filterable
  36. @expand-change="setIndex(index)"
  37. clearable
  38. :options="rule_list"
  39. @change="ruleChange"
  40. :props="{ children: 'child', label: 'name', value: 'id', checkStrictly: true }"
  41. ></el-cascader>
  42. </el-form-item>
  43. <el-form-item
  44. label="选择规则"
  45. v-if="item.rule_switch"
  46. :prop="'items.' + index + '.rule_item_list_value'"
  47. :rules="[{ required: true, message: '请选择规则', trigger: 'blur' }]"
  48. >
  49. <el-cascader
  50. v-model="item.rule_item_list_value"
  51. ref="ruleItem"
  52. :popper-class="'itemClass'"
  53. filterable
  54. clearable
  55. @expand-change="setIndex(index)"
  56. :options="rule_item_list"
  57. @change="ruleItemChange"
  58. :show-all-levels="false"
  59. :props="{ children: 'child', label: 'name', value: 'id' }"
  60. ></el-cascader>
  61. </el-form-item>
  62. <el-form-item>
  63. <div style="line-height: 24px;" v-show="item.rule_item_details.range_type == 1">
  64. {{ item.rule_item_details.min_point }}
  65. <span class="blue">{{ $getTypsName(ptid) }}</span>
  66. </div>
  67. <div style="line-height: 24px;" v-show="item.rule_item_details.range_type == 2">
  68. {{ item.rule_item_details.min_point }} ~ {{ item.rule_item_details.max_point }}
  69. <span class="blue">{{ $getTypsName(ptid) }}</span>
  70. </div>
  71. <div style="line-height: 24px;">{{ item.rule_item_details.name }}</div>
  72. </el-form-item>
  73. <el-form-item label="积分" :prop="'items.' + index + '.point'" :rules="[{ required: true, message: '请输入分值', trigger: 'blur' }]">
  74. <el-input-number v-if="item.rule_item_details.range_type == 2" :min="item.min" :max="item.max" v-model.number="item.point"></el-input-number>
  75. <el-input-number v-else :disabled="item.rule_item_details.range_type == 1" v-model.number="item.point"></el-input-number>
  76. </el-form-item>
  77. <el-form-item label="发生时间" :prop="'items.' + index + '.event_time'" :rules="[{ required: true, message: '请选择时间', trigger: 'blur' }]">
  78. <el-row>
  79. <el-col :span="18">
  80. <el-date-picker
  81. v-model="item.event_time"
  82. :picker-options="pickerBeginDateBefore"
  83. type="date"
  84. placeholder="请选择时间"
  85. value-format="yyyy-MM-dd"
  86. ></el-date-picker>
  87. </el-col>
  88. </el-row>
  89. </el-form-item>
  90. <el-form-item
  91. label="事件内容"
  92. :prop="'items.' + index + '.remark'"
  93. :rules="[{ required: true, message: '请输入事件内容', trigger: 'blur' }, { min: 2, max: 300, message: '长度在 2 到 300 个字符', trigger: 'blur' }]"
  94. >
  95. <el-row>
  96. <el-col :span="18"><el-input type="textarea" placeholder="请输入事件内容" style="width: 100%;" :rows="5" maxlength="300" v-model="item.remark" ></el-input></el-col>
  97. </el-row>
  98. </el-form-item>
  99. <el-form-item label="图片">
  100. <upload
  101. :headers="Xtoken"
  102. class="avatar-uploader"
  103. :action="'https://' + 'integralsys.oss-cn-shenzhen.aliyuncs.com'"
  104. :show-file-list="true"
  105. :file-list="item.fileList"
  106. :on-success="handleFilesSuccess"
  107. :on-preview="onFilePreView"
  108. :before-upload="beforeUpload"
  109. :on-remove="onFileRemove"
  110. :limit="3"
  111. accept="image/jpeg,image/png"
  112. :multiple="true"
  113. ref="clearPicture"
  114. >
  115. <el-button size="small" type="primary">点击上传</el-button>
  116. (最多选择3张)
  117. </upload>
  118. </el-form-item>
  119. <el-form-item label="递交审批" v-if="$getUserData().is_creator == 0">
  120. <el-row>
  121. <el-col :span="18">
  122. <el-input auto-complete="off" v-model="item.approvalName" placeholder="请选择审批人"></el-input>
  123. <div @click="item.show_approval_selector = true" style=" position: absolute; top: 0; right: 0; left: 0; bottom: 0; z-index: 9;"></div>
  124. </el-col>
  125. </el-row>
  126. <el-dialog title="选择人员" width="640px" :visible.sync="item.show_approval_selector" append-to-body :before-close="handleClose">
  127. <EmployeeSelector
  128. v-if="item.show_approval_selector"
  129. ref="superior"
  130. :multi="false"
  131. :isCreatorSelect="true"
  132. :user_no_select="false"
  133. :employee_list="superior_list"
  134. :selected="item.approval_selected"
  135. @confirm="approval_confirm"
  136. />
  137. <span slot="footer" class="dialog-footer">
  138. <el-button @click="item.show_approval_selector = false">取 消</el-button>
  139. <el-button type="primary" @click="submitEmployee('superior')">确 定</el-button>
  140. </span>
  141. </el-dialog>
  142. </el-form-item>
  143. </div>
  144. <el-form-item style="margin-bottom: 0;"><div>如需录入多条,请点击“增加一条”</div></el-form-item>
  145. <el-form-item style="margin-bottom: 0;"><el-button type="primary" plain @click="addItem">+ 增加一条</el-button></el-form-item>
  146. <el-form-item style="text-align: right; margin-bottom: 0;">
  147. <el-button @click="closeDialog('dialogData')" :disabled="btn_loading">取 消</el-button>
  148. <el-button type="primary" @click="subData('dialogData')" :disabled="btn_loading" :loading="btn_loading">确 认</el-button>
  149. </el-form-item>
  150. </el-form>
  151. </div>
  152. <el-dialog title="选择人员" width="640px" :visible.sync="show_employee_selector" append-to-body :before-close="handleClose">
  153. <EmployeeSelector
  154. v-if="show_employee_selector"
  155. ref="members"
  156. :isChecKedAll="false"
  157. :isCreatorSelect="true"
  158. :user_no_select="false"
  159. :max="30"
  160. :employee_list="manage_scope"
  161. :selected="employee_selected"
  162. @confirm="move_employee_confirm"
  163. />
  164. <span slot="footer" class="dialog-footer">
  165. <el-button @click="show_employee_selector = false">取 消</el-button>
  166. <el-button type="primary" @click="submitMembers()">确 定</el-button>
  167. </span>
  168. </el-dialog>
  169. </el-dialog>
  170. <el-dialog :title="'提交结果'" :visible.sync="error_list_show" :append-to-body="true" width="700px">
  171. <el-table :data="error_list">
  172. <el-table-column prop="target" label="员工"></el-table-column>
  173. <el-table-column prop="point" label="积分">
  174. <template slot-scope="scope">
  175. <span>
  176. {{ scope.row.point }}
  177. <span>{{ integralType == 2 ? 'A分' : 'B分' }}</span>
  178. </span>
  179. </template>
  180. </el-table-column>
  181. <el-table-column prop="status" label="处理状态">
  182. <template slot-scope="scope">
  183. <span :style="'color:' + (scope.row.status == 0 ? '#f70000' : '#47bf47')">{{ scope.row.msg }}</span>
  184. </template>
  185. </el-table-column>
  186. <el-table-column prop="remark" label="备注信息"></el-table-column>
  187. </el-table>
  188. </el-dialog>
  189. <el-dialog title="提交结果" :visible.sync="isResult" width="800" :close-on-click-modal="false" :close-on-press-escape="false" :show-close="false">
  190. <div v-if="!isShowError">
  191. <div style="text-align: center;margin-bottom: 10px;" class="red" v-if="isShowError2">{{errorMsg}}</div>
  192. <el-progress :text-inside="true" :stroke-width="24" :percentage="percentage"></el-progress>
  193. <div class="orange" style="text-align: center;padding-top: 10px;" v-if="config.event_review_status&&config.event_entry_review">复核开启后,积分需管理员复核后才计入排名和统计</div>
  194. <div style="margin-top: 20px;border: 1px solid #f1f1f1;max-height: 500px;overflow-y: auto;" class="scroll-bar">
  195. <div class="flex-box-ce results" style="font-weight: 600;">
  196. <div style="border-right: 1px solid #f1f1f1;width: 50px;">序号</div>
  197. <div class="flex-1" style="border-right: 1px solid #f1f1f1;">奖扣对象</div>
  198. <div class="flex-2" style="border-right: 1px solid #f1f1f1;">事件内容</div>
  199. <div class="flex-1" style="border-right: 1px solid #f1f1f1;">积分</div>
  200. <div class="flex-2" >处理结果</div>
  201. </div>
  202. <div class="flex-box-ce results" v-for="(item, index) in results" :key="index">
  203. <div style="border-right: 1px solid #f1f1f1;width: 50px;">{{results.length-index}}</div>
  204. <div class="flex-1" style="border-right: 1px solid #f1f1f1;">{{ item.target }}</div>
  205. <div class="flex-2" style="border-right: 1px solid #f1f1f1;">{{ item.task.msg.remark }}</div>
  206. <div class="flex-1" style="border-right: 1px solid #f1f1f1;">
  207. {{ item.point>0? '+'+item.point:item.point }}
  208. <span>{{ item.task.msg.pt_id==3? 'B分':'A分' }}</span>
  209. </div>
  210. <div class="flex-2" v-if="item.status == 1">
  211. <span v-if="item.msg=='奖扣成功'" class="green">{{ item.msg }}</span>
  212. <span v-else class="blue">{{ item.msg }}</span>
  213. </div>
  214. <div class="flex-2 red" v-else>{{ item.msg }}</div>
  215. </div>
  216. </div>
  217. <span slot="footer">
  218. <div class="flex-box-end" style="margin-top: 20px;" v-show="isShowError2&&results.length!=resultList.length"><el-button type="primary" @click="isResult = false" size="small">确 定</el-button></div>
  219. <div class="flex-box-end" style="margin-top: 20px;" v-show="results.length==resultList.length"><el-button type="primary" @click="isResult = false" size="small">确 定</el-button></div>
  220. </span>
  221. </div>
  222. <div v-else>
  223. <div style="text-align: center;" class="red">{{errorMsg}}</div>
  224. <span slot="footer">
  225. <div class="flex-box-end" style="margin-top: 20px;"><el-button type="primary" @click="isResult = false" size="small">确 定</el-button></div>
  226. </span>
  227. </div>
  228. </el-dialog>
  229. <!-- 缓存的奖扣 -->
  230. <el-dialog title="网络中断奖扣列表" :visible.sync="isShowBreak" width="800" :close-on-click-modal="false" :close-on-press-escape="false" :show-close="false">
  231. <div>
  232. <div style="margin-top: 20px;border: 1px solid #f1f1f1;max-height: 500px;overflow-y: auto;" class="scroll-bar">
  233. <div class="flex-box-ce results" style="font-weight: 600;">
  234. <div class="flex-1" style="border-right: 1px solid #f1f1f1;">奖扣对象</div>
  235. <div class="flex-1" style="border-right: 1px solid #f1f1f1;">分类</div>
  236. <div class="flex-1" style="border-right: 1px solid #f1f1f1;">规则</div>
  237. <div class="flex-2" style="border-right: 1px solid #f1f1f1;">事件内容</div>
  238. <div class="flex-1" style="border-right: 1px solid #f1f1f1;">积分</div>
  239. <div class="flex-1" style="border-right: 1px solid #f1f1f1;">递交人员</div>
  240. </div>
  241. <div class="flex-box-ce results" v-for="(item, index) in breakList" :key="index">
  242. <div class="flex-1" style="border-right: 1px solid #f1f1f1;">{{ item.name }}</div>
  243. <div class="flex-1" style="border-right: 1px solid #f1f1f1;">
  244. <span v-if="item.rule_name">{{ item.rule_name }}</span>
  245. <span v-else>--</span>
  246. </div>
  247. <div class="flex-1" style="border-right: 1px solid #f1f1f1;">
  248. <span v-if="item.item_name">{{ item.item_name }}</span>
  249. <span v-else>--</span>
  250. </div>
  251. <div class="flex-2" style="border-right: 1px solid #f1f1f1;">{{ item.remark }}</div>
  252. <div class="flex-1" style="border-right: 1px solid #f1f1f1;">{{ item.point }}
  253. <span>{{ item.pt_id==3? 'B分':'A分' }}</span>
  254. </div>
  255. <div class="flex-1" style="border-right: 1px solid #f1f1f1;">{{ item.reviewer_name }}</div>
  256. </div>
  257. </div>
  258. <span slot="footer">
  259. <div class="flex-box-end" style="margin-top: 20px;">
  260. <el-button type="primary" @click="colseBreak()" size="small">取 消</el-button>
  261. <el-button type="primary" @click="submitBreak()" size="small">再次提交</el-button>
  262. </div>
  263. </span>
  264. </div>
  265. </el-dialog>
  266. </div>
  267. </template>
  268. <script>
  269. import moment from 'moment';
  270. import EmployeeSelector from '@/components/EmployeeSelector.vue';
  271. import upload from '@/components/upload';
  272. export default {
  273. name: 'bonusPointsForm',
  274. // 数据
  275. model: {
  276. prop: 'list',
  277. event: 'value'
  278. },
  279. props: {
  280. title: {
  281. type: String,
  282. default: ''
  283. },
  284. visible: {
  285. type: Boolean,
  286. default: false
  287. },
  288. isBreak: { //是否打开缓存的未完成奖扣
  289. type: Boolean,
  290. default: false
  291. },
  292. refresh: {
  293. type: String,
  294. default: ''
  295. },
  296. integralType: {
  297. type: Number,
  298. default: 0
  299. // 1 是绩效分 , 2 是A分 , 3 是B分
  300. },
  301. ruleOnoff: {
  302. type: Boolean,
  303. default: false
  304. //true:选择规则; false:选择分类
  305. }
  306. },
  307. data() {
  308. return {
  309. pickerBeginDateBefore: {
  310. disabledDate(time) {
  311. return time.getTime() > Date.now();
  312. }
  313. },
  314. Xtoken: { 'X-Token': this.$getToken() },
  315. btn_loading: false,
  316. loading: false,
  317. dialogData: {
  318. members: [],
  319. items: [
  320. {
  321. rule_switch: true,
  322. rule_id: '',
  323. item_id: '',
  324. point: '0',
  325. remark: '',
  326. event_time: moment().format('YYYY-MM-DD'),
  327. approval: '',
  328. approval_not_select: [],
  329. pt_id: this.integralType,
  330. // 积分填写限制
  331. pointShow: 1,
  332. max: 0,
  333. min: 0,
  334. // 审批人信息
  335. reviewer_id: '',
  336. approvalName: '',
  337. approval_not_select: [],
  338. approval_selected: { dept: [], employee: [] },
  339. show_approval_selector: false,
  340. // 附件
  341. fileList: [],
  342. files: [],
  343. // 规则分类 与 规则细则 名称
  344. rule_list_value: null,
  345. rule_item_list_value: null,
  346. // 规则细则详情
  347. rule_item_details: { range_type: '' }
  348. }
  349. ]
  350. },
  351. // 奖扣对象名称
  352. employeeName: '',
  353. employee_not_select: [],
  354. employee_selected: { dept: [], employee: [] },
  355. manage_scope: [], //下属人员
  356. superior_list: [], //上级人员
  357. show_employee_selector: false,
  358. // 规则分类
  359. rule_list: [],
  360. // 规则细则
  361. rule_item_list: [],
  362. flatteningIntegralRules: null,
  363. ptid: 0,
  364. itemIndex: 0,
  365. // 错误提示
  366. error_list: [], //错误信息数组
  367. error_list_show: false ,//错误信息弹窗
  368. // 长连接结果
  369. results: [], //提交的返回结果集合
  370. isResult: false,
  371. percentage: 0,
  372. resultList:[],//要发送数据的集合
  373. resultIndex:0,
  374. isShowError:false,
  375. isShowError2:false,
  376. errorMsg:'服务器繁忙,请稍后再试',
  377. breakList:[],
  378. isShowBreak:false,
  379. config:{},
  380. };
  381. },
  382. components: { EmployeeSelector, upload },
  383. watch: {
  384. isBreak(){
  385. let data=this.$getCache('award_punish').obj;
  386. data.forEach(item=>{
  387. item.name=this.$getCache('userList')[item.employee_id]?this.$getCache('userList')[item.employee_id].name:'--';
  388. item.reviewer_name=this.$getCache('userList')[item.reviewer_id]?this.$getCache('userList')[item.reviewer_id].name:'--';
  389. })
  390. this.breakList=data;
  391. this.isShowBreak=true;
  392. },
  393. integralType(val) {
  394. this.dialogData.items[0].pt_id = val;
  395. this.ptid = val;
  396. this.getRuleData();
  397. this.getRuleItemData();
  398. },
  399. 'dialogData.members'(val) {
  400. if (val.length == 0) {
  401. this.employeeName = '';
  402. this.employee_selected = { dept: [], employee: [] };
  403. }
  404. },
  405. isResult(val){
  406. if(!val){
  407. this.isShowError = false;
  408. this.isShowBreak = false;
  409. this.$refs['dialogData'].resetFields();
  410. this.$emit('update:refresh',this.$moment().format().valueOf());
  411. this.closePopup();
  412. this.errorMsg='服务器繁忙,请稍后再试';
  413. this.$socketApi.closewebsocket();
  414. }
  415. },
  416. },
  417. mounted() {
  418. var that = this;
  419. this.config=this.$store.state.config;
  420. this.getUserDetail(function(data) {
  421. that.manage_scope = data.manage_scope;
  422. that.superior_list = data.superior_list;
  423. });
  424. },
  425. methods: {
  426. setIndex(index){
  427. if(index==this.itemIndex){
  428. return false
  429. }
  430. this.itemIndex = index;
  431. },
  432. // 关闭缓存弹窗
  433. colseBreak(){
  434. this.isShowBreak = false;
  435. this.breakList=[];
  436. this.$removeCache('award_punish');
  437. this.$emit('update:refresh',this.$moment().format().valueOf());
  438. this.closePopup();
  439. },
  440. // 提交缓存奖扣
  441. submitBreak(){
  442. this.$removeCache('award_punish');
  443. this.resultList=JSON.parse(JSON.stringify(this.breakList));
  444. this.resultIndex=0;
  445. this.percentage=0;
  446. this.results=[];
  447. this.isResult=true;
  448. this.opneWebSocket()
  449. },
  450. getUserDetail(func) {
  451. this.$axios('get', '/api/employee/detail').then(res => {
  452. this.$setUserData(res.data.data.user);
  453. func(res.data.data.user.employee_detail);
  454. });
  455. },
  456. submitEmployee(name) {
  457. this.$refs[name][0].confirm(); //调用组件的confirm();
  458. },
  459. submitMembers() {
  460. this.$refs.members.confirm(); //调用组件的confirm();
  461. },
  462. //关闭
  463. handleClose(done) {
  464. done();
  465. },
  466. // 附件上传
  467. beforeUpload(file) {
  468. const isJPG = /^image\/(jpeg|png|jpg)$/.test(file.type);
  469. const isLt2M = file.size / 1024 / 1024 < 5;
  470. if (!isJPG) {
  471. this.$message.error('上传图只能是 JPEG,PNG,JPG 格式!');
  472. }
  473. if (!isLt2M) {
  474. this.$message.error('上传图片大小不能超过 5MB!');
  475. }
  476. return isJPG && isLt2M;
  477. },
  478. onFilePreView(file) {
  479. if (file.response) {
  480. window.open(file.response.url, '_blank');
  481. }
  482. },
  483. onFileRemove(file, fileList) {
  484. this.dialogData.items[this.itemIndex].fileList = fileList;
  485. this.dialogData.items[this.itemIndex].files = [];
  486. fileList.forEach((element, index) => {
  487. this.dialogData.items[this.itemIndex].files.push(element.url);
  488. });
  489. },
  490. handleFilesSuccess(response, file, fileList) {
  491. let fileListData=fileList.filter(e=>{
  492. return e.url
  493. })
  494. this.dialogData.items[this.itemIndex].fileList = fileListData;
  495. this.dialogData.items[this.itemIndex].files = [];
  496. fileListData.forEach((element, index) => {
  497. this.dialogData.items[this.itemIndex].files.push(element.url);
  498. });
  499. },
  500. // 没有人员提示
  501. noPersonnelListTips() {
  502. this.show_employee_selector = true;
  503. },
  504. // 当switch 改变了
  505. switchChange(index, value) {
  506. this.itemIndex = index;
  507. const item = this.dialogData.items[this.itemIndex];
  508. if (!value) {
  509. item.remark = '';
  510. item.rule_id = '';
  511. item.item_id = '';
  512. item.rule_item_list_value = '';
  513. item.rule_item_details = { range_type: '' };
  514. item.max = 0;
  515. item.min = 0;
  516. item.point = '0';
  517. } else {
  518. item.rule_id = '';
  519. item.rule_list_value = '';
  520. }
  521. },
  522. // 选择奖扣对象
  523. move_employee_confirm(data) {
  524. this.employee_selected = { dept: [], employee: [] };
  525. this.employeeName = '';
  526. this.dialogData.members = [];
  527. if (data.employee !== null && data.employee.length != 0) {
  528. let nameArr = [];
  529. data.employee.forEach(element => {
  530. this.employeeName += element.name + ',';
  531. this.employee_selected = data;
  532. this.dialogData.members.push(element.id);
  533. });
  534. }
  535. this.show_employee_selector = false;
  536. },
  537. // 获取规则类型
  538. getRuleData() {
  539. let data = { cycle_type: '1', pt_id: this.integralType };
  540. this.$axios('get', '/api/integral/rule/trees', data).then(res => {
  541. if (res.data.code == 1) {
  542. this.rule_list = this.getTreeData(res.data.data.rule_tree);
  543. }
  544. });
  545. },
  546. // 获取规则细则
  547. getRuleItemData() {
  548. let data = { cycle_type: '1', pt_id: this.integralType };
  549. this.$axios('get', '/api/integral/rule/trees', data, 'v2').then(res => {
  550. if (res.data.code == 1) {
  551. const resultData = res.data.data;
  552. this.rule_item_list = resultData.tree;
  553. this.flatteningIntegralRules = this.getItemDetail(this.rule_item_list);
  554. this.loading = false;
  555. }
  556. });
  557. },
  558. // 规则细则变化关闭down
  559. ruleItemChange(value) {
  560. const item = this.dialogData.items[this.itemIndex];
  561. if (value.length > 0) {
  562. let ruleItemDetails = null;
  563. this.flatteningIntegralRules.forEach(element => {
  564. if (element.id == value[value.length - 1]) {
  565. ruleItemDetails = { ...element };
  566. }
  567. });
  568. item.rule_item_details = ruleItemDetails;
  569. item.remark = ruleItemDetails.name;
  570. item.rule_id = ruleItemDetails.pid;
  571. item.item_id = value[value.length - 1];
  572. item.max = ruleItemDetails.max_point * 1;
  573. item.min = ruleItemDetails.min_point * 1;
  574. item.point = ruleItemDetails.min_point;
  575. } else {
  576. item.remark = '';
  577. item.rule_id = '';
  578. item.item_id = '';
  579. item.rule_item_list_value = '';
  580. item.rule_item_details = { range_type: '' };
  581. item.max = 0;
  582. item.min = 0;
  583. item.point = '0';
  584. }
  585. },
  586. // 规则分类变化关闭dewn
  587. ruleChange(value) {
  588. const item = this.dialogData.items[this.itemIndex];
  589. if (value.length > 0) {
  590. const user_info = this.$getUserData();
  591. let ruleCascader='ruleCascader'+this.itemIndex;
  592. user_info.point_config.point_limit.forEach(element => {
  593. if (this.ptid == this.integralType) {
  594. item.max = element.point * 1;
  595. item.min = element.point * -1;
  596. }
  597. });
  598. this.$refs.[ruleCascader][0].dropDownVisible = false;
  599. item.rule_id = value[value.length - 1];
  600. item.rule_name = this.$refs.[ruleCascader][0].getCheckedNodes()[0].label;
  601. } else {
  602. item.rule_id = '';
  603. item.rule_list_value = '';
  604. item.rule_name='';
  605. }
  606. },
  607. // 递归判断列表,把最后的child设为undefined
  608. getTreeData(data) {
  609. for (var i = 0; i < data.length; i++) {
  610. if (data[i].child.length < 1) {
  611. // child若为空数组,则将child设为undefined
  612. data[i].child = undefined;
  613. } else {
  614. // child若不为空数组,则继续 递归调用 本方法
  615. this.getTreeData(data[i].child);
  616. }
  617. }
  618. return data;
  619. },
  620. getItemDetail(arr) {
  621. let result = [];
  622. for (const item of arr) {
  623. var res = JSON.parse(JSON.stringify(item)); // 先克隆一份数据作为第一层级的填充
  624. delete res['child'];
  625. result.push(res);
  626. if (item.child instanceof Array && item.child.length > 0) {
  627. // 如果当前child为数组并且长度大于0,才可进入getItemDetail()方法
  628. result = result.concat(this.getItemDetail(item.child));
  629. }
  630. }
  631. return result;
  632. },
  633. // 加一条
  634. addItem() {
  635. if (this.dialogData.items.length == 10) {
  636. this.$message({
  637. type: 'warning',
  638. message: '一次只能添加10条奖扣'
  639. });
  640. return false;
  641. }
  642. this.dialogData.items.push({
  643. rule_switch: true,
  644. rule_id: '',
  645. item_id: '',
  646. point: '0',
  647. remark: '',
  648. event_time: moment().format('YYYY-MM-DD'),
  649. approval: '',
  650. approval_not_select: [],
  651. pt_id: this.integralType,
  652. // 积分填写限制
  653. pointShow: 1,
  654. max: 0,
  655. min: 0,
  656. // 审批人信息
  657. reviewer_id: '',
  658. approvalName: '',
  659. approval_not_select: [],
  660. approval_selected: { dept: [], employee: [] },
  661. show_approval_selector: false,
  662. // 附件
  663. fileList: [],
  664. files: [],
  665. // 规则分类 与 规则细则 名称
  666. rule_list_value: null,
  667. rule_item_list_value: null,
  668. // 规则细则详情
  669. rule_item_details: { range_type: '' }
  670. });
  671. },
  672. closeDialog(formName) {
  673. this.dialogData.items.forEach(element => {
  674. element.rule_item_details = { range_type: '' };
  675. });
  676. this.$refs[formName].resetFields();
  677. this.closePopup();
  678. },
  679. delItem(index) {
  680. this.$confirm('你确定要删除奖扣明细' + parseInt(index + 1) + '吗?', '提示', {
  681. confirmButtonText: '确定',
  682. cancelButtonText: '取消',
  683. type: 'warning'
  684. }).then(() => {
  685. this.dialogData.items.splice(index, 1);
  686. this.$message({
  687. type: 'success',
  688. message: '删除成功!'
  689. });
  690. });
  691. },
  692. subData(formName) {
  693. this.$refs[formName].validate(valid => {
  694. if (valid) {
  695. this.save();
  696. }
  697. });
  698. },
  699. // 提交数据
  700. save() {
  701. let data = {
  702. members: this.dialogData.members,
  703. items: []
  704. };
  705. const user_info = this.$getUserData();
  706. const index = user_info.point_config.point_limit.findIndex(o => o.pt_id === this.integralType);
  707. let employeePointLimitMin = null;
  708. let employeePointLimitMax = null;
  709. user_info.point_config.point_limit.forEach(element => {
  710. if (this.ptid == this.integralType) {
  711. employeePointLimitMax = element.point * 1;
  712. employeePointLimitMin = element.point * 1;
  713. }
  714. });
  715. const ruleLimitCheck = user_info.site_config.rule_limit_check;
  716. let maxPointPermission = 0;
  717. let creator = user_info.employee_detail.role_list.findIndex(item => item.name == 'creator') >= 0;
  718. if (user_info.point_config.point_limit.length > 0) {
  719. maxPointPermission = parseInt(user_info.point_config.point_limit[index].point);
  720. } else {
  721. if (creator) {
  722. maxPointPermission = -1;
  723. }
  724. }
  725. try {
  726. if (index < 0 || user_info.is_creator === 1) {
  727. this.dialogData.items.forEach((element, i) => {
  728. data.items.push({
  729. rule_id: element.rule_id || 0,
  730. item_id: element.item_id || 0,
  731. point: element.point,
  732. remark: element.remark,
  733. event_time: element.event_time,
  734. pt_id: this.ptid,
  735. rule_name:element.rule_name,
  736. item_name:element.rule_item_details.name,
  737. reviewer_id: element.reviewer_id || 0,
  738. files: element.files
  739. });
  740. });
  741. } else {
  742. this.dialogData.items.forEach((element, index) => {
  743. !element.reviewer_id ? (element.reviewer_id = 0) : '';
  744. !element.item_id ? (element.item_id = 0) : '';
  745. if (
  746. (element.reviewer_id && element.point !== 0 && element.rule_id > 0) ||
  747. (element.reviewer_id <= 0 &&
  748. element.item_id > 0 &&
  749. ruleLimitCheck &&
  750. element.point !== 0 &&
  751. element.point <= maxPointPermission &&
  752. Math.abs(element.point) <= maxPointPermission) ||
  753. (element.reviewer_id <= 0 && !ruleLimitCheck && this.integralType == 3 && element.rule_id > 0) ||
  754. (element.reviewer_id <= 0 &&
  755. element.item_id >= 0 &&
  756. element.point !== 0 &&
  757. element.point <= maxPointPermission &&
  758. Math.abs(element.point) <= maxPointPermission &&
  759. element.rule_id > 0)
  760. ) {
  761. data.items.push({
  762. rule_id: element.rule_id || 0,
  763. item_id: element.item_id || 0,
  764. point: element.point,
  765. remark: element.remark,
  766. event_time: element.event_time,
  767. pt_id: this.ptid,
  768. reviewer_id: element.reviewer_id || 0,
  769. files: element.files,
  770. rule_name:element.rule_name,
  771. item_name:element.rule_item_details.name,
  772. });
  773. } else {
  774. this.$message.error('第' + (index + 1) + '条输入积分分值超出权限,请选择审批人递交');
  775. throw new Error();
  776. }
  777. });
  778. }
  779. } catch (e) {
  780. this.btn_loading = false;
  781. return false;
  782. }
  783. this.webSocket(data);
  784. // 走长连接处理
  785. return false
  786. this.btn_loading = true;
  787. this.$axios('post', this.integralType === 1 ? '' : this.integralType === 2 ? '/api/integral/review/a/entry' : '/api/integral/point/entry', data)
  788. .then(res => {
  789. if (res.data.code == 1) {
  790. var is = true,msg;
  791. if (this.integralType == '3') {
  792. res.data.data.list.forEach(item => {
  793. if (item.status != 1) {
  794. is = false;
  795. msg = item.msg;
  796. }
  797. });
  798. } else {
  799. res.data.data.list.forEach(item => {
  800. if (item.status != 1) {
  801. is = false;
  802. msg = item.rule_item;
  803. }
  804. });
  805. }
  806. if (is) {
  807. this.dialogData.items.forEach(element => {
  808. element.rule_item_details = { range_type: '' };
  809. });
  810. this.$refs['dialogData'].resetFields();
  811. this.$emit('update:visible', false);
  812. this.$emit('update:refresh',this.$moment().format().valueOf());
  813. this.closePopup();
  814. this.error_list = res.data.data.list;
  815. this.error_list_show = true;
  816. }
  817. }
  818. })
  819. .finally(e => {
  820. this.btn_loading = false;
  821. });
  822. },
  823. webSocket(data){
  824. let {members,items}=data;
  825. let arr=[];
  826. members.forEach(item=>{
  827. items.forEach(item2=>{
  828. item2.type = this.integralType === 2?'pea':'peb';
  829. item2.employee_id=item;
  830. arr.push(JSON.parse(JSON.stringify(item2)))
  831. })
  832. })
  833. this.resultList=arr;
  834. this.resultIndex=0;
  835. this.percentage=0;
  836. this.results=[];
  837. this.isResult=true;
  838. this.opneWebSocket()
  839. },
  840. opneWebSocket() {
  841. let wsData=this.resultList;
  842. if(wsData[this.resultIndex]&&!this.isShowError){
  843. this.$socketApi.sendData(wsData[this.resultIndex],this.onmessageWS)
  844. }
  845. },
  846. onmessageWS(e){
  847. if(e.type=='peb'||e.type=='pea'){
  848. this.results.unshift(e.result);
  849. this.resultIndex++;
  850. this.opneWebSocket();
  851. // 进度条
  852. let lng = this.resultList.length;
  853. this.percentage += Math.floor(100 / lng);
  854. if (lng == this.results.length) {
  855. this.percentage = 100;
  856. }
  857. }
  858. // 中途断开
  859. if(e.type=='break'){
  860. let wsData=this.resultList;
  861. this.errorMsg=e.msg
  862. let data={
  863. type:this.integralType,
  864. obj:wsData.slice(this.resultIndex,wsData.length)
  865. }
  866. this.$setCache('award_punish',data);
  867. this.isShowError2 = true;
  868. }
  869. // 连接不上
  870. if(e.type=='error'){
  871. this.errorMsg=e.msg
  872. this.isShowError = true;
  873. }
  874. },
  875. // 选择审核人
  876. approval_confirm(data) {
  877. const item = this.dialogData.items[this.itemIndex];
  878. item.approvalName = '';
  879. item.approval_selected.employee = { dept: [], employee: [] };
  880. item.reviewer_id = '';
  881. if (data.employee !== null && data.employee.length != 0) {
  882. item.approvalName = data.employee[0].name;
  883. item.approval_selected.employee = [{ name: data.employee[0].name, id: data.employee[0].id, img_url: data.employee[0].img_url }];
  884. item.reviewer_id = data.employee[0].id;
  885. }
  886. item.reviewer_id ? (item.pointShow = 3) : '';
  887. item.show_approval_selector = false;
  888. },
  889. // 关闭弹窗
  890. closePopup() {
  891. //关闭重置窗口状态
  892. this.dialogData.items = [
  893. {
  894. rule_switch: true,
  895. rule_id: '',
  896. item_id: '',
  897. point: '0',
  898. remark: '',
  899. event_time: moment().format('YYYY-MM-DD'),
  900. approval: '',
  901. approval_not_select: [],
  902. pt_id: this.integralType,
  903. // 积分填写限制
  904. pointShow: 1,
  905. max: 0,
  906. min: 0,
  907. // 审批人信息
  908. reviewer_id: '',
  909. approvalName: '',
  910. approval_not_select: [],
  911. approval_selected: { dept: [], employee: [] },
  912. show_approval_selector: false,
  913. // 附件
  914. fileList: [],
  915. files: [],
  916. // 规则分类 与 规则细则 名称
  917. rule_list_value: null,
  918. rule_item_list_value: null,
  919. // 规则细则详情
  920. rule_item_details: { range_type: '' }
  921. }
  922. ];
  923. this.$nextTick(() => {
  924. this.$emit('update:visible', false);
  925. });
  926. }
  927. }
  928. };
  929. </script>
  930. <style lang="scss">
  931. .itemClass .el-cascader-menu .el-cascader-menu__wrap .el-scrollbar__view li.el-cascader-node {
  932. height: auto;
  933. max-width: 500px;
  934. .el-cascader-node__label {
  935. white-space: initial;
  936. overflow: initial;
  937. text-overflow: initial;
  938. }
  939. }
  940. .results {
  941. border-bottom: 1px solid #f1f1f1;
  942. text-align: center;
  943. }
  944. .results div {
  945. padding: 10px;
  946. }
  947. </style>