Index.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. <template>
  2. <section class="panel">
  3. <div class="toolbar">
  4. <el-select v-model="query.storeId" placeholder="门店" clearable filterable style="width: 180px;">
  5. <el-option v-for="s in stores" :key="s.storeId" :label="s.storeName" :value="s.storeId" />
  6. </el-select>
  7. <el-select v-model="query.indicatorId" placeholder="指标" clearable filterable style="width: 180px;">
  8. <el-option v-for="i in indicators" :key="i.indicatorId" :label="i.indicatorName" :value="i.indicatorId" />
  9. </el-select>
  10. <el-select v-model="query.status" placeholder="整改状态" clearable>
  11. <el-option label="待整改" :value="1" />
  12. <el-option label="整改中" :value="2" />
  13. <el-option label="待复检" :value="3" />
  14. <el-option label="复检通过" :value="4" />
  15. <el-option label="复检不通过" :value="5" />
  16. <el-option label="已超期" :value="6" />
  17. <el-option label="最终不通过" :value="7" />
  18. </el-select>
  19. <el-date-picker v-model="deadlineRange" type="daterange" value-format="yyyy-MM-dd" start-placeholder="截止开始" end-placeholder="截止结束" style="width: 240px;" />
  20. <el-button type="primary" @click="load()">查询</el-button>
  21. <el-button @click="reset">重置</el-button>
  22. <span class="grow"></span>
  23. <el-button type="warning" plain :disabled="!selected.length" @click="batchVisible = true">批量设置截止日期({{ selected.length }})</el-button>
  24. <el-button type="primary" plain @click="loadAll()">刷新</el-button>
  25. </div>
  26. <!-- 整改统计 -->
  27. <div class="stat-cards">
  28. <div class="stat-card" v-for="c in statItems" :key="c.label">
  29. <span>{{ c.label }}</span>
  30. <b :style="{ color: c.color }">{{ c.value }}</b>
  31. <em>条</em>
  32. </div>
  33. </div>
  34. <el-table :data="rows" border stripe v-loading="loading" @selection-change="onSelect">
  35. <el-table-column type="selection" width="45" />
  36. <el-table-column prop="rectId" label="ID" width="75" />
  37. <el-table-column prop="storeName" label="门店" min-width="130" />
  38. <el-table-column prop="indicatorName" label="指标" min-width="120" />
  39. <el-table-column label="轮次" width="70" align="center">
  40. <template slot-scope="s">第 {{ s.row.rectRound || 1 }} 轮</template>
  41. </el-table-column>
  42. <el-table-column label="巡检/及格" width="100">
  43. <template slot-scope="s">{{ fmtNum(s.row.inspectScore) }} / {{ fmtNum(s.row.passScore) }}</template>
  44. </el-table-column>
  45. <el-table-column label="截止日期" width="115">
  46. <template slot-scope="s">{{ fmt(s.row.deadlineDate, true) }}</template>
  47. </el-table-column>
  48. <el-table-column label="反馈日期" width="115">
  49. <template slot-scope="s">{{ fmt(s.row.feedbackDate, true) }}</template>
  50. </el-table-column>
  51. <el-table-column label="复检日期" width="115">
  52. <template slot-scope="s">{{ fmt(s.row.recheckDate, true) }}</template>
  53. </el-table-column>
  54. <el-table-column label="催办" width="80" align="center">
  55. <template slot-scope="s">
  56. <span v-if="s.row.urgeCount">{{ s.row.urgeCount }} 次</span>
  57. <span v-else class="muted">-</span>
  58. </template>
  59. </el-table-column>
  60. <el-table-column label="状态" width="100">
  61. <template slot-scope="s">
  62. <el-tag size="mini" :style="rectStyle(s.row.status)">{{ rectText(s.row.status) }}</el-tag>
  63. </template>
  64. </el-table-column>
  65. <el-table-column label="操作" width="150" fixed="right">
  66. <template slot-scope="s">
  67. <el-button type="text" @click="openDetail(s.row)">详情</el-button>
  68. <el-button type="text" v-if="canUrge(s.row)" @click="urge(s.row)">催办</el-button>
  69. <el-button type="text" v-if="s.row.status === 3" @click="openRecheck(s.row)">复检</el-button>
  70. </template>
  71. </el-table-column>
  72. </el-table>
  73. <div class="pager">
  74. <el-pagination
  75. background
  76. layout="total, sizes, prev, pager, next, jumper"
  77. :total="total"
  78. :page-size="query.pageSize"
  79. :current-page="query.currentPage"
  80. @size-change="n => { query.pageSize = n; load() }"
  81. @current-change="n => { query.currentPage = n; load() }"
  82. />
  83. </div>
  84. <el-dialog title="批量设置截止日期" :visible.sync="batchVisible" width="420px">
  85. <el-form label-width="100px">
  86. <el-form-item label="已选记录">{{ selected.length }} 条</el-form-item>
  87. <el-form-item label="截止日期" required>
  88. <el-date-picker v-model="batchDeadline" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="选择截止日期时间" style="width: 100%;" />
  89. </el-form-item>
  90. </el-form>
  91. <span slot="footer">
  92. <el-button @click="batchVisible = false">取消</el-button>
  93. <el-button type="primary" :loading="batchSaving" @click="batchUpdate">确定</el-button>
  94. </span>
  95. </el-dialog>
  96. <rect-detail :visible.sync="detailVisible" :row="detailRow" @recheck="openRecheck" @saved="load" />
  97. <rect-recheck :visible.sync="recheckVisible" :row="recheckRow" @saved="onRechecked" />
  98. </section>
  99. </template>
  100. <script>
  101. import { post, ok, msg, pageData, fmt, fmtNum, slashDate, cleanParams, RECT_STATUS } from '../common'
  102. import RectDetail from './Detail.vue'
  103. import RectRecheck from './Recheck.vue'
  104. export default {
  105. name: 'InspectRectificationIndex',
  106. components: { RectDetail, RectRecheck },
  107. data() {
  108. return {
  109. query: { storeId: '', indicatorId: '', status: '', deadlineDateBegin: '', deadlineDateEnd: '', pageSize: 10, currentPage: 1 },
  110. deadlineRange: [],
  111. rows: [],
  112. total: 0,
  113. loading: false,
  114. stats: { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0 },
  115. selected: [],
  116. stores: [],
  117. indicators: [],
  118. batchVisible: false,
  119. batchDeadline: '',
  120. batchSaving: false,
  121. detailVisible: false,
  122. detailRow: null,
  123. recheckVisible: false,
  124. recheckRow: null
  125. }
  126. },
  127. computed: {
  128. statItems() {
  129. const defs = [
  130. { key: 1, label: '待整改', color: '#E6A23C' },
  131. { key: 2, label: '整改中', color: '#409EFF' },
  132. { key: 3, label: '待复检', color: '#9B59B6' },
  133. { key: 6, label: '已超期', color: '#909399' },
  134. { key: 4, label: '复检通过', color: '#67C23A' }
  135. ]
  136. return defs.map(d => ({ label: d.label, color: d.color, value: this.stats[d.key] || 0 }))
  137. }
  138. },
  139. created() {
  140. this.load()
  141. this.loadOptions()
  142. this.loadStats()
  143. },
  144. methods: {
  145. loadAll() {
  146. this.load()
  147. this.loadStats()
  148. },
  149. loadStats() {
  150. post(this.$axios, '/inspectManage/rectification/page', { pageSize: 500, currentPage: 1 }).then(res => {
  151. const list = (res && res.list) || []
  152. const count = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0 }
  153. list.forEach(r => { if (count[r.status] !== undefined) count[r.status]++ })
  154. this.stats = count
  155. })
  156. },
  157. load() {
  158. const q = Object.assign({}, this.query)
  159. q.deadlineDateBegin = this.deadlineRange && this.deadlineRange[0] ? slashDate(this.deadlineRange[0]) : ''
  160. q.deadlineDateEnd = this.deadlineRange && this.deadlineRange[1] ? slashDate(this.deadlineRange[1]) : ''
  161. this.loading = true
  162. post(this.$axios, '/inspectManage/rectification/page', cleanParams(q)).then(res => {
  163. this.rows = (res && res.list) || []
  164. this.total = pageData(res).resultCount || this.rows.length
  165. }).finally(() => { this.loading = false })
  166. },
  167. loadOptions() {
  168. post(this.$axios, '/storeManage/getStoreList', {}).then(res => { this.stores = (res && res.list) || [] })
  169. post(this.$axios, '/inspectManage/indicator/page', { pageSize: 200, currentPage: 1 }).then(res => { this.indicators = (res && res.list) || [] })
  170. },
  171. reset() {
  172. this.query = { storeId: '', indicatorId: '', status: '', deadlineDateBegin: '', deadlineDateEnd: '', pageSize: 10, currentPage: 1 }
  173. this.deadlineRange = []
  174. this.load()
  175. },
  176. onSelect(rows) { this.selected = rows },
  177. canUrge(row) { return [1, 2, 3, 5, 6].indexOf(row.status) !== -1 },
  178. urge(row) {
  179. this.$confirm('确认对该整改进行催办?', '提示').then(() => {
  180. post(this.$axios, '/inspectManage/rectification/urge', { id: row.rectId }).then(res => {
  181. if (ok(res)) { this.load(); this.$message.success('催办完成') } else { this.$message.error(msg(res) || '催办失败') }
  182. })
  183. }).catch(() => {})
  184. },
  185. openDetail(row) {
  186. this.detailRow = row
  187. this.detailVisible = true
  188. },
  189. openRecheck(row) {
  190. this.recheckRow = row
  191. this.recheckVisible = true
  192. },
  193. onRechecked() {
  194. this.recheckVisible = false
  195. this.load()
  196. },
  197. batchUpdate() {
  198. if (!this.batchDeadline) return this.$message.warning('请选择截止日期')
  199. this.batchSaving = true
  200. post(this.$axios, '/inspectManage/rectification/batchUpdateDeadline', {
  201. rectIds: this.selected.map(r => r.rectId),
  202. deadlineDate: this.batchDeadline
  203. }).then(res => {
  204. if (ok(res)) {
  205. this.batchVisible = false
  206. this.batchDeadline = ''
  207. this.load()
  208. this.$message.success('截止日期更新完成')
  209. } else {
  210. this.$message.error(msg(res) || '更新失败')
  211. }
  212. }).finally(() => { this.batchSaving = false })
  213. },
  214. rectText(v) { return (RECT_STATUS[v] || {}).text || '-' },
  215. rectStyle(v) {
  216. const st = RECT_STATUS[v]
  217. return { background: st.color, borderColor: st.color, color: '#fff' }
  218. },
  219. fmt,
  220. fmtNum
  221. }
  222. }
  223. </script>
  224. <style scoped>
  225. .panel { background: #fff; border: 1px solid #ebeef5; border-radius: 4px; padding: 18px; }
  226. .toolbar { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 16px; }
  227. .toolbar .el-select, .toolbar .el-input { width: 160px; }
  228. .grow { flex: 1; }
  229. .muted { color: #909399; }
  230. .pager { margin-top: 16px; text-align: right; }
  231. .stat-cards { display: flex; gap: 16px; margin-bottom: 16px; }
  232. .stat-card { flex: 1; background: #f5f7fa; border-radius: 6px; padding: 14px 18px; display: flex; align-items: baseline; gap: 8px; }
  233. .stat-card span { color: #909399; font-size: 13px; }
  234. .stat-card b { font-size: 24px; color: #303133; }
  235. .stat-card em { color: #909399; font-style: normal; font-size: 12px; }
  236. </style>