You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

389 lines
19 KiB

2 months ago
  1. <template>
  2. <div class="customer-css">
  3. <!-- 查询条件 -->
  4. <el-form :inline="true" label-position="top" class="pi-search-form">
  5. <el-form-item label="任务单号">
  6. <el-input v-model="searchData.requestNo" style="width:140px" @keyup.enter.native="getList" />
  7. </el-form-item>
  8. <el-form-item>
  9. <span style="cursor: pointer" slot="label" @click="getBaseList(501)"><a href="#">供应商编码</a></span>
  10. <el-input v-model="searchData.supplierNo" style="width: 130px"></el-input>
  11. </el-form-item>
  12. <el-form-item label="供应商名称">
  13. <el-input v-model="searchData.supplierName" style="width:200px" @keyup.enter.native="getList" />
  14. </el-form-item>
  15. <el-form-item label="创建者">
  16. <el-input v-model="searchData.createBy" style="width:140px" @keyup.enter.native="getList" />
  17. </el-form-item>
  18. <el-form-item label="预计完成时间">
  19. <el-date-picker v-model="searchData.wantedCompleteDateStart" type="date" placeholder="Start Date"
  20. value-format="yyyy-MM-dd"
  21. style="width:140px" />
  22. -
  23. <el-date-picker v-model="searchData.wantedCompleteDateEnd" type="date" placeholder="End Date"
  24. value-format="yyyy-MM-dd"
  25. style="width:140px" />
  26. </el-form-item>
  27. <el-form-item label="创建时间">
  28. <el-date-picker v-model="searchData.createDateStart" type="date" placeholder="Start Date"
  29. value-format="yyyy-MM-dd HH:mm:ss"
  30. style="width:140px" />
  31. -
  32. <el-date-picker v-model="searchData.createDateEnd" type="date" placeholder="End Date"
  33. value-format="yyyy-MM-dd HH:mm:ss"
  34. style="width:140px" />
  35. </el-form-item>
  36. <el-form-item label="状态">
  37. <el-select v-model="searchData.statusList" multiple collapse-tags clearable style="width:150px" placeholder="All">
  38. <el-option
  39. v-for="item in reviewStatusOptions"
  40. :key="item.value"
  41. :label="item.label"
  42. :value="item.value" />
  43. </el-select>
  44. </el-form-item>
  45. <el-form-item label=" ">
  46. <el-button type="primary" class="customer-bun-min" @click="getList">查询</el-button>
  47. </el-form-item>
  48. </el-form>
  49. <!-- 列表 -->
  50. <el-table :data="dataList" :height="requestTableHeight - 100" border highlight-current-row v-loading="loading"
  51. @row-click="rowClick" @current-change="handleMainCurrentChange" ref="mainTable" style="width:100%;margin-bottom:8px">
  52. <el-table-column fixed="right" label="操作" width="80" header-align="center" align="center">
  53. <template slot-scope="scope">
  54. <a v-if="canOperate(scope.row)" class="customer-a" @click.stop="openReviewDialog(scope.row)">审核</a>
  55. </template>
  56. </el-table-column>
  57. <el-table-column prop="requestNo" label="任务单号" min-width="120" header-align="center" align="left" />
  58. <el-table-column prop="supplierNo" label="供应商编码" width="120" header-align="center" align="left" />
  59. <el-table-column prop="supplierName" label="供应商名称" min-width="200" header-align="center" align="left" show-overflow-tooltip />
  60. <el-table-column prop="wantedCompleteDate" label="预计完成时间" width="160" header-align="center" align="center" />
  61. <el-table-column prop="status" label="状态" width="90" header-align="center" align="center" />
  62. <el-table-column prop="reviewBy" label="审核人" min-width="90" header-align="center" align="left" />
  63. <el-table-column prop="reviewDate" label="审核时间" width="130" header-align="center" align="center" />
  64. <el-table-column prop="approveBy" label="批准人" min-width="90" header-align="center" align="left" />
  65. <el-table-column prop="approveDate" label="批准时间" width="130" header-align="center" align="center" />
  66. <el-table-column prop="comments" label="备注" min-width="160" header-align="center" align="left" />
  67. <el-table-column prop="createBy" label="创建者" min-width="100" header-align="center" align="left" />
  68. <el-table-column prop="createDate" label="创建时间" width="130" header-align="center" align="center" />
  69. </el-table>
  70. <el-pagination style="margin-top:0" @size-change="sizeChangeHandle" @current-change="currentChangeHandle"
  71. :current-page="pageIndex" :page-sizes="[20,50,100,200,500]" :page-size="pageSize" :total="totalPage"
  72. layout="total, sizes, prev, pager, next, jumper" />
  73. <!-- 指标 + Progress -->
  74. <el-row :gutter="10" style="margin-top:12px">
  75. <el-col :span="16">
  76. <div class="sub-panel-header">
  77. <span style="font-size:16px">考核单</span>
  78. </div>
  79. <el-table :data="indicatorList" size="mini" border :height="indicatorTableHeight" highlight-current-row
  80. ref="indicatorTable" v-loading="indicatorLoading" @row-click="selectIndicatorRow">
  81. <el-table-column prop="indicatorCode" label="指标代码" min-width="120" header-align="center" align="left" show-overflow-tooltip />
  82. <el-table-column prop="indicatorDesc" label="指标描述" min-width="220" header-align="center" align="left" show-overflow-tooltip />
  83. <el-table-column prop="category" label="指标分类" width="110" header-align="center" align="center" />
  84. <el-table-column prop="ownerRole" label="负责部门" width="110" header-align="center" align="center" />
  85. <el-table-column prop="maxScore" label="最高分" width="110" header-align="center" align="right" />
  86. <el-table-column prop="score" label="分数" width="90" header-align="center" align="right" />
  87. </el-table>
  88. </el-col>
  89. <el-col :span="8">
  90. <div class="sub-panel-header">
  91. <span style="font-size:16px">进度 <span v-if="viewIndicatorCode"></span></span>
  92. </div>
  93. <el-table :data="progressList" size="mini" border :height="indicatorTableHeight" v-loading="progressLoading">
  94. <el-table-column prop="departmentName" label="部门" min-width="90" header-align="center"
  95. align="left" />
  96. <el-table-column prop="owner" label="负责人" min-width="80" header-align="center" align="left" />
  97. <el-table-column prop="userDisplay" label="负责人名称" min-width="90" header-align="center" align="left" />
  98. <el-table-column prop="completedFlag" label="已完成" width="60" header-align="center" align="center">
  99. </el-table-column>
  100. <el-table-column prop="completedDate" label="完成时间" min-width="140" header-align="center"
  101. align="center" />
  102. </el-table>
  103. </el-col>
  104. </el-row>
  105. <!-- Review 弹窗 -->
  106. <el-dialog title="审核" :visible.sync="reviewDialogVisible" width="374px" top="8vh" :close-on-click-modal="false"
  107. v-drag>
  108. <el-form :inline="true" label-position="top" class="create-top-form">
  109. <el-form-item
  110. label="任务单号"><el-input v-model="reviewForm.requestNo" disabled style="width: 120px"/></el-form-item>
  111. <el-form-item label="审核人"><el-input v-model="reviewForm.createBy" disabled style="width: 120px"/></el-form-item>
  112. </el-form>
  113. <el-form :inline="true" label-position="top" size="mini" class="create-top-form">
  114. <el-form-item label="供应商编号"><el-input v-model="reviewForm.supplierNo" disabled style="width: 120px"/></el-form-item>
  115. <el-form-item label="供应商名称"><el-input v-model="reviewForm.supplierName" disabled
  116. style="width: 210px"/></el-form-item>
  117. </el-form>
  118. <el-form :inline="true" label-position="top" size="mini" class="create-top-form">
  119. <el-form-item label="备注" :required="true">
  120. <el-input type="textarea" :rows="2" v-model="reviewForm.comments" style="width: 343px" />
  121. </el-form-item>
  122. </el-form>
  123. <span slot="footer" class="dialog-footer">
  124. <el-button type="primary" :loading="reviewSaving" @click="saveReview">审核</el-button>
  125. <el-button type="danger" :loading="voidSaving" @click="voidReview">作废</el-button>
  126. <el-button @click="reviewDialogVisible=false">取消</el-button>
  127. </span>
  128. </el-dialog>
  129. <Chooselist ref="baseList" @getBaseData="getBaseData"></Chooselist>
  130. </div>
  131. </template>
  132. <script>
  133. import { searchEvaluateRequestList, getEvaluateRequestIndicators, getEvaluateRequestIndicatorProgress } from '@/api/supplierPerformance/myPerformanceEvaluateRequest.js'
  134. import { updateEvaluateRequestReviewOrApprove } from '@/api/supplierPerformance/myPerformanceEvaluateRequest'
  135. import {
  136. PERF_REVIEW_STATUS_OPTIONS,
  137. PERF_REVIEW_STATUS_DEFAULT
  138. } from './supplierPerformanceStatusOptions.js'
  139. import Chooselist from '@/views/modules/common/Chooselist_eam'
  140. export default {
  141. components: {
  142. Chooselist,
  143. },
  144. name: 'PerformanceEvaluateRequestReview',
  145. data() {
  146. return {
  147. height: 500,
  148. loading: false,
  149. dataList: [],
  150. pageIndex: 1,
  151. pageSize: 20,
  152. totalPage: 0,
  153. reviewStatusOptions: PERF_REVIEW_STATUS_OPTIONS,
  154. searchData: {
  155. site: this.$store.state.user.site,
  156. username: this.$store.state.user.name,
  157. requestNo: '',
  158. supplierNo: '',
  159. supplierName: '',
  160. createBy: '',
  161. wantedCompleteDateStart: '',
  162. wantedCompleteDateEnd: '',
  163. createDateStart: '',
  164. createDateEnd: '',
  165. statusList: ['已完成']
  166. },
  167. currentRequest: {},
  168. indicatorList: [],
  169. indicatorLoading: false,
  170. reviewDialogVisible: false,
  171. reviewSaving: false,
  172. voidSaving: false,
  173. reviewForm: { id:'', requestNo:'', supplierNo:'', supplierName:'', createBy:'', comments:'' },
  174. viewIndicatorCode: '',
  175. progressList: [],
  176. progressLoading: false,
  177. progressRequestKey: '',
  178. }
  179. },
  180. computed: {
  181. requestTableHeight() { return this.height - 360 < 260 ? 260 : this.height - 360 },
  182. indicatorTableHeight() { return 300 }
  183. },
  184. mounted() { this.calcHeight(); window.addEventListener('resize', this.calcHeight); this.getList() },
  185. beforeDestroy() { window.removeEventListener('resize', this.calcHeight) },
  186. methods: {
  187. getBaseList (val) {
  188. this.tagNo = val
  189. this.$nextTick(() => {
  190. let strVal = ''
  191. let conSql = ''
  192. if (val === 501) {
  193. strVal = this.searchData.supplierNo || ''
  194. conSql = " and site = '" + this.$store.state.user.site + "'"
  195. }
  196. this.$refs.baseList.init(val, strVal, conSql)
  197. })
  198. },
  199. getBaseData (val) {
  200. if (this.tagNo === 501) {
  201. const supplierNoVal = val.SupplierID || val.supplierID || val.supplierId || ''
  202. this.searchData.supplierNo = supplierNoVal
  203. }
  204. },
  205. calcHeight() { this.$nextTick(()=> { this.height = window.innerHeight - 40 }) },
  206. clearSubDetails() {
  207. this.currentRequest = {}
  208. this.indicatorList = []
  209. this.viewIndicatorCode = ''
  210. this.progressList = []
  211. this.progressRequestKey = ''
  212. this.$nextTick(() => {
  213. if (this.$refs.mainTable) this.$refs.mainTable.setCurrentRow(null)
  214. if (this.$refs.indicatorTable) this.$refs.indicatorTable.setCurrentRow(null)
  215. })
  216. },
  217. getList() {
  218. this.loading = true
  219. if (this.searchData.statusList.length === 0) this.searchData.statusList = [...PERF_REVIEW_STATUS_DEFAULT]
  220. const params = { ...this.searchData, statusList: this.searchData.statusList , page: this.pageIndex, limit: this.pageSize, selectType: 1 }
  221. searchEvaluateRequestList(params).then(({data})=> {
  222. if (data && (data.code===0||data.code==='0')) {
  223. if (data.page) {
  224. this.dataList = data.page.list || []
  225. this.pageIndex = data.page.currPage || params.page
  226. this.pageSize = data.page.pageSize || params.limit
  227. this.totalPage = data.page.totalCount || 0
  228. if (this.dataList.length > 0) {
  229. this.$nextTick(() => {
  230. this.$refs.mainTable.setCurrentRow(this.dataList[0])
  231. this.rowClick(this.dataList[0])
  232. })
  233. } else {
  234. this.clearSubDetails()
  235. }
  236. } else {
  237. this.dataList = data.rows || []
  238. this.totalPage = data.total || this.dataList.length
  239. if (this.dataList.length === 0) this.clearSubDetails()
  240. }
  241. } else {
  242. this.dataList = []
  243. this.totalPage = 0
  244. this.clearSubDetails()
  245. }
  246. }).catch(() => {
  247. this.dataList = []
  248. this.totalPage = 0
  249. this.clearSubDetails()
  250. })
  251. .finally(()=> { this.loading=false })
  252. },
  253. sizeChangeHandle(v){ this.pageSize=v; this.pageIndex=1; this.getList() },
  254. currentChangeHandle(v){ this.pageIndex=v; this.getList() },
  255. handleMainCurrentChange(row) {
  256. if (!row) this.clearSubDetails()
  257. },
  258. rowClick(row) {
  259. if (!row) {
  260. this.clearSubDetails()
  261. return
  262. }
  263. this.currentRequest = row
  264. this.viewIndicatorCode = ''
  265. this.progressList = []
  266. this.loadIndicators()
  267. },
  268. loadIndicators() {
  269. if (!this.currentRequest.requestNo) {
  270. this.indicatorList = []
  271. this.viewIndicatorCode = ''
  272. this.progressList = []
  273. return
  274. }
  275. this.indicatorLoading = true
  276. this.indicatorList = []
  277. this.viewIndicatorCode = ''
  278. this.progressList = []
  279. const p = { requestId: this.currentRequest.id, requestNo: this.currentRequest.requestNo, site: this.$store.state.user.site }
  280. getEvaluateRequestIndicators(p).then(({ data }) => {
  281. if (data && (data.code === 0 || data.code === '0')) {
  282. this.indicatorList = data.list || []
  283. this.$nextTick(() => {
  284. if (this.indicatorList.length > 0) {
  285. this.$refs.indicatorTable.setCurrentRow(this.indicatorList[0])
  286. this.selectIndicatorRow(this.indicatorList[0])
  287. } else {
  288. this.viewIndicatorCode = ''
  289. this.progressList = []
  290. this.$refs.indicatorTable && this.$refs.indicatorTable.setCurrentRow(null)
  291. }
  292. })
  293. } else {
  294. this.indicatorList = []
  295. this.viewIndicatorCode = ''
  296. this.progressList = []
  297. }
  298. }).catch(() => {
  299. this.indicatorList = []
  300. this.viewIndicatorCode = ''
  301. this.progressList = []
  302. }).finally(() => { this.indicatorLoading = false })
  303. },
  304. selectIndicatorRow(row) {
  305. if (!row) {
  306. this.viewIndicatorCode = ''
  307. this.progressList = []
  308. return
  309. }
  310. this.viewIndicatorCode = row.indicatorCode
  311. this.progressList = []
  312. this.loadProgress()
  313. },
  314. loadProgress() {
  315. if (!this.viewIndicatorCode || !this.currentRequest.requestNo) {
  316. this.progressList = []
  317. return
  318. }
  319. const requestKey = `${this.currentRequest.id || ''}_${this.currentRequest.requestNo}_${this.viewIndicatorCode}`
  320. this.progressRequestKey = requestKey
  321. this.progressLoading = true
  322. const p = { requestId: this.currentRequest.id, requestNo: this.currentRequest.requestNo, site: this.$store.state.user.site }
  323. getEvaluateRequestIndicatorProgress(p).then(({ data }) => {
  324. if (this.progressRequestKey !== requestKey) return
  325. if (data && (data.code === 0 || data.code === '0')) {
  326. this.progressList = data.list || []
  327. } else {
  328. this.progressList = []
  329. }
  330. }).catch(() => {
  331. if (this.progressRequestKey !== requestKey) return
  332. this.progressList = []
  333. }).finally(() => { this.progressLoading = false })
  334. },
  335. canOperate(row){
  336. if(!row) return false;
  337. const s=(row.status||'').trim();
  338. return s==='已完成';
  339. },
  340. openReviewDialog(row){
  341. this.reviewForm = { id:row.id, requestNo:row.requestNo, supplierNo:row.supplierNo, supplierName:row.supplierName, createBy:row.createBy, comments: row.reviewComments || '' }
  342. this.reviewDialogVisible = true
  343. },
  344. saveReview(){
  345. if (this.reviewSaving || this.voidSaving) return
  346. const c = (this.reviewForm.comments||'').trim()
  347. if(!c){ this.$message.warning('Comments不能为空'); return }
  348. this.reviewSaving = true
  349. const payload = { site:this.$store.state.user.site, id:this.reviewForm.id, requestNo:this.reviewForm.requestNo,
  350. comments:c, updateBy:this.$store.state.user.name, updateType: 1 }
  351. updateEvaluateRequestReviewOrApprove(payload).then(({data})=> {
  352. if (data && (data.code===0||data.code==='0')) { this.$message.success('审核成功'); this.reviewDialogVisible=false; this.getList() } else { this.$message.error((data&&data.msg)||'审核失败') }
  353. }).catch(e=> this.$message.error('审核失败: '+ e.message)).finally(()=> { this.reviewSaving=false })
  354. },
  355. voidReview() {
  356. if (this.reviewSaving || this.voidSaving) return
  357. this.$confirm('是否作废该绩效审核单?', '提示', { type: 'warning' }).then(() => {
  358. this.voidSaving = true
  359. const payload = {
  360. site: this.$store.state.user.site,
  361. id: this.reviewForm.id,
  362. requestNo: this.reviewForm.requestNo,
  363. updateBy: this.$store.state.user.name,
  364. updateType: 4
  365. }
  366. updateEvaluateRequestReviewOrApprove(payload).then(({ data }) => {
  367. if (data && (data.code === 0 || data.code === '0')) {
  368. this.$message.success('作废成功')
  369. this.reviewDialogVisible = false
  370. this.getList()
  371. } else {
  372. this.$message.error((data && data.msg) || '作废失败')
  373. }
  374. }).catch(e => this.$message.error('作废失败: ' + e.message)).finally(() => { this.voidSaving = false })
  375. }).catch(() => {})
  376. }
  377. }
  378. }
  379. </script>
  380. <style scoped>
  381. .pi-search-form { margin-top:0; }
  382. .sub-panel-header { display:flex; align-items:center; justify-content:space-between; font-weight:600; padding:4px 6px; background:#f5f7fa; border:1px solid #ebeef5; border-bottom:none; }
  383. </style>