| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- <template>
- <section class="panel">
- <div class="toolbar">
- <el-select v-model="query.planId" placeholder="巡检计划" clearable filterable style="width: 180px;">
- <el-option v-for="p in plans" :key="p.planId" :label="p.planName" :value="p.planId" />
- </el-select>
- <el-select v-model="query.storeId" placeholder="所属门店" clearable filterable style="width: 180px;">
- <el-option v-for="s in stores" :key="s.storeId" :label="s.storeName" :value="s.storeId" />
- </el-select>
- <el-input v-model="query.inspectorId" placeholder="巡检员ID(我的任务)" clearable style="width:150px;" @keyup.enter.native="load()" />
- <el-select v-model="query.status" placeholder="任务状态" clearable>
- <el-option label="待执行" :value="1" />
- <el-option label="执行中" :value="2" />
- <el-option label="已提交" :value="3" />
- <el-option label="已取消" :value="4" />
- </el-select>
- <el-select v-model="query.passFlag" placeholder="考核结果" clearable>
- <el-option label="及格" :value="1" />
- <el-option label="不及格" :value="0" />
- </el-select>
- <el-date-picker v-model="dateRange" type="daterange" value-format="yyyy-MM-dd" start-placeholder="开始日期" end-placeholder="结束日期" style="width: 240px;" />
- <el-button type="primary" @click="load()">查询</el-button>
- <el-button @click="reset">重置</el-button>
- <span class="grow"></span>
- <el-button type="primary" plain @click="loadAll()">刷新</el-button>
- </div>
- <!-- 任务统计 -->
- <div class="stat-cards">
- <div class="stat-card"><span>任务总数</span><b>{{ stats.total }}</b><em>条</em></div>
- <div class="stat-card"><span>已完成</span><b>{{ stats.finish }}</b><em>条 · 完成率 {{ stats.finishRate }}%</em></div>
- <div class="stat-card"><span>及格率</span><b>{{ stats.passRate }}</b><em>%</em></div>
- <div class="stat-card"><span>平均得分</span><b>{{ stats.avg }}</b><em>分</em></div>
- </div>
- <el-table :data="rows" border stripe v-loading="loading">
- <el-table-column prop="taskId" label="任务ID" width="80" />
- <el-table-column prop="planName" label="巡检计划" min-width="130" />
- <el-table-column prop="storeName" label="门店" min-width="140" />
- <el-table-column label="执行日期" width="110">
- <template slot-scope="s">{{ fmt(s.row.taskDate) }}</template>
- </el-table-column>
- <el-table-column prop="inspectorName" label="巡检员" width="90">
- <template slot-scope="s">{{ s.row.inspectorName || '-' }}</template>
- </el-table-column>
- <el-table-column label="得分" width="150">
- <template slot-scope="s">
- <div v-if="s.row.status === 3" class="score-cell">
- <el-progress :percentage="scorePct(s.row)" :status="scoreStatus(s.row)" :stroke-width="10" :show-text="false" />
- <span class="score-text">{{ fmtNum(s.row.totalScore) }}/{{ fmtNum(s.row.maxScore) }}</span>
- </div>
- <span v-else class="muted">-</span>
- </template>
- </el-table-column>
- <el-table-column label="结果" width="85">
- <template slot-scope="s">
- <el-tag v-if="s.row.status === 3" size="mini" :type="s.row.passFlag === 1 ? 'success' : 'danger'">{{ s.row.passFlag === 1 ? '及格' : '不及格' }}</el-tag>
- <span v-else class="muted">-</span>
- </template>
- </el-table-column>
- <el-table-column prop="unpassItemCount" label="不合格项" width="85" align="center">
- <template slot-scope="s">{{ s.row.unpassItemCount || 0 }}</template>
- </el-table-column>
- <el-table-column label="状态" width="90">
- <template slot-scope="s"><el-tag size="mini" :type="taskStatus(s.row.status).type">{{ taskStatus(s.row.status).text }}</el-tag></template>
- </el-table-column>
- <el-table-column label="操作" width="200" fixed="right">
- <template slot-scope="s">
- <el-button type="text" v-if="s.row.status === 1 || s.row.status === 2" @click="openExecute(s.row)">执行</el-button>
- <el-button type="text" v-if="s.row.status === 3" @click="openReport(s.row)">报告</el-button>
- <el-button type="text" v-if="s.row.status === 1 || s.row.status === 2" class="danger" @click="cancel(s.row)">取消</el-button>
- <el-button type="text" v-if="s.row.status === 1 || s.row.status === 4" class="danger" @click="remove(s.row)">删除</el-button>
- </template>
- </el-table-column>
- </el-table>
- <div class="pager">
- <el-pagination
- background
- layout="total, sizes, prev, pager, next, jumper"
- :total="total"
- :page-size="query.pageSize"
- :current-page="query.currentPage"
- @size-change="n => { query.pageSize = n; load() }"
- @current-change="n => { query.currentPage = n; load() }"
- />
- </div>
- <task-execute :visible.sync="executeVisible" :task-id="currentId" @saved="onSaved" />
- <task-report :visible.sync="reportVisible" :task-id="currentId" />
- </section>
- </template>
- <script>
- import { post, ok, msg, pageData, fmt, fmtNum, slashDate, cleanParams, TASK_STATUS } from '../common'
- import TaskExecute from './Execute.vue'
- import TaskReport from './Report.vue'
- export default {
- name: 'InspectTaskIndex',
- components: { TaskExecute, TaskReport },
- data() {
- return {
- query: { planId: '', storeId: '', inspectorId: '', status: '', passFlag: '', taskDateBegin: '', taskDateEnd: '', pageSize: 10, currentPage: 1 },
- dateRange: [],
- rows: [],
- total: 0,
- loading: false,
- stats: { total: 0, finish: 0, finishRate: 0, passRate: 0, avg: '0.0' },
- plans: [],
- stores: [],
- executeVisible: false,
- reportVisible: false,
- currentId: null
- }
- },
- created() {
- this.load()
- this.loadOptions()
- this.loadStats()
- },
- methods: {
- loadAll() {
- this.load()
- this.loadStats()
- },
- load() {
- const q = Object.assign({}, this.query)
- q.taskDateBegin = this.dateRange && this.dateRange[0] ? slashDate(this.dateRange[0]) : ''
- q.taskDateEnd = this.dateRange && this.dateRange[1] ? slashDate(this.dateRange[1]) : ''
- this.loading = true
- post(this.$axios, '/inspectTaskManage/getTaskPage', cleanParams(q)).then(res => {
- this.rows = (res && res.list) || []
- this.total = pageData(res).resultCount || this.rows.length
- }).finally(() => { this.loading = false })
- },
- loadStats() {
- post(this.$axios, '/inspectTaskManage/getTaskStatistics', {}).then(res => {
- const rows = (res && res.list) || []
- let total = 0
- let finish = 0
- let pass = 0
- let scoreSum = 0
- rows.forEach(r => {
- const arr = Array.isArray(r) ? r : Object.keys(r).map(k => r[k])
- const count = Number(arr[1] || 0)
- const finishCount = Number(arr[2] || 0)
- const passCount = Number(arr[3] || 0)
- const avgScore = Number(arr[4] || 0)
- total += count
- finish += finishCount
- pass += passCount
- scoreSum += avgScore * count
- })
- this.stats = {
- total,
- finish,
- finishRate: total ? Math.round(finish / total * 100) : 0,
- passRate: finish ? Math.round(pass / finish * 100) : 0,
- avg: total ? (scoreSum / total).toFixed(1) : '0.0'
- }
- })
- },
- loadOptions() {
- post(this.$axios, '/inspectPlanManage/getPlanPage', { pageSize: 200, currentPage: 1 }).then(res => {
- this.plans = (res && res.list) || []
- })
- post(this.$axios, '/storeManage/getStoreList', {}).then(res => {
- this.stores = (res && res.list) || []
- })
- },
- reset() {
- this.query = { planId: '', storeId: '', inspectorId: '', status: '', passFlag: '', taskDateBegin: '', taskDateEnd: '', pageSize: 10, currentPage: 1 }
- this.dateRange = []
- this.load()
- },
- openExecute(row) {
- this.currentId = row.taskId
- this.executeVisible = true
- },
- openReport(row) {
- this.currentId = row.taskId
- this.reportVisible = true
- },
- onSaved() {
- this.executeVisible = false
- this.load()
- },
- cancel(row) {
- this.$confirm('确认取消该任务?', '提示').then(() => {
- post(this.$axios, '/inspectTaskManage/cancelTask', { id: row.taskId }).then(res => {
- if (ok(res)) { this.load(); this.$message.success('取消完成') } else { this.$message.error(msg(res) || '取消失败') }
- })
- }).catch(() => {})
- },
- remove(row) {
- this.$confirm('确认删除该任务?仅待执行或已取消的任务可删除。', '提示').then(() => {
- post(this.$axios, '/inspectTaskManage/deleteTask', { id: row.taskId }).then(res => {
- if (ok(res)) { this.load(); this.$message.success('删除完成') } else { this.$message.error(msg(res) || '删除失败') }
- })
- }).catch(() => {})
- },
- scorePct(row) {
- const max = Number(row.maxScore || 0)
- return max > 0 ? Math.round(Number(row.totalScore || 0) / max * 100) : 0
- },
- scoreStatus(row) {
- return row.passFlag === 1 ? 'success' : 'exception'
- },
- taskStatus(v) { return TASK_STATUS[v] || { text: '-', type: 'info' } },
- fmt,
- fmtNum
- }
- }
- </script>
- <style scoped>
- .panel { background: #fff; border: 1px solid #e4e7ed; border-radius: 6px; padding: 20px; box-shadow: 0 1px 2px rgba(0,0,0,0.04); }
- .toolbar { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 16px; }
- .toolbar .el-select, .toolbar .el-input { width: 160px; }
- .grow { flex: 1; }
- .danger { color: #f56c6c; }
- .muted { color: #909399; }
- .pager { margin-top: 16px; text-align: right; }
- .score-cell { display: flex; align-items: center; gap: 8px; }
- .score-cell .el-progress { flex: 1; }
- .score-text { width: 70px; font-size: 12px; color: #606266; white-space: nowrap; }
- .stat-cards { display: flex; gap: 16px; margin-bottom: 16px; }
- .stat-card { flex: 1; background: #f5f7fa; border-radius: 6px; padding: 14px 18px; display: flex; align-items: baseline; gap: 8px; }
- .stat-card span { color: #909399; font-size: 13px; }
- .stat-card b { font-size: 24px; color: #303133; }
- .stat-card em { color: #909399; font-style: normal; font-size: 12px; }
- </style>
|