Commit 5b6e99a524d1c9a0cf9081efccd76d79569997b3
1 parent
0c63f3b7
```
feat(attendance): 新增考勤数据批量删除和导入功能 新增了考勤数据的批量删除和 Excel 导入功能,包括对应的弹窗组件 BatchDeleteDialog 和 ImportDialog。 同时在门店管理模块中增加了门店类别、门店类型、新店阶段等字段及相应接口支持,并完善了相关表单与列表展示逻辑。 此外,在项目资料模块中添加了“是否有效”字段用于标识数据有效性状态。 ```
Showing
10 changed files
with
1439 additions
and
22 deletions
antis-ncc-admin/src/views/attendance/BatchDeleteDialog.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog | |
| 3 | + title="批量删除考勤数据" | |
| 4 | + :visible.sync="visible" | |
| 5 | + width="400px" | |
| 6 | + :close-on-click-modal="false" | |
| 7 | + @close="close" | |
| 8 | + > | |
| 9 | + <div> | |
| 10 | + <el-form :model="form" label-width="80px" label-position="right" :rules="rules" ref="form"> | |
| 11 | + <el-form-item label="年份" prop="year"> | |
| 12 | + <el-select v-model="form.year" placeholder="请选择年份" style="width: 100%"> | |
| 13 | + <el-option v-for="year in yearOptions" :key="year" :label="year + '年'" :value="year" /> | |
| 14 | + </el-select> | |
| 15 | + </el-form-item> | |
| 16 | + <el-form-item label="月份" prop="month"> | |
| 17 | + <el-select v-model="form.month" placeholder="请选择月份" style="width: 100%"> | |
| 18 | + <el-option v-for="month in monthOptions" :key="month" :label="month + '月'" :value="month" /> | |
| 19 | + </el-select> | |
| 20 | + </el-form-item> | |
| 21 | + </el-form> | |
| 22 | + | |
| 23 | + <el-alert | |
| 24 | + title="警告" | |
| 25 | + type="warning" | |
| 26 | + description="此操作将删除指定年月下的所有考勤数据,删除后无法恢复,请谨慎操作!" | |
| 27 | + show-icon | |
| 28 | + :closable="false" | |
| 29 | + style="margin-top: 20px;" | |
| 30 | + /> | |
| 31 | + </div> | |
| 32 | + | |
| 33 | + <div slot="footer" class="dialog-footer"> | |
| 34 | + <el-button @click="close">取消</el-button> | |
| 35 | + <el-button type="danger" @click="confirmDelete" :loading="deleting"> | |
| 36 | + 确定删除 | |
| 37 | + </el-button> | |
| 38 | + </div> | |
| 39 | + </el-dialog> | |
| 40 | +</template> | |
| 41 | + | |
| 42 | +<script> | |
| 43 | +import request from '@/utils/request' | |
| 44 | + | |
| 45 | +export default { | |
| 46 | + name: 'BatchDeleteDialog', | |
| 47 | + data() { | |
| 48 | + return { | |
| 49 | + visible: false, | |
| 50 | + deleting: false, | |
| 51 | + form: { | |
| 52 | + year: null, | |
| 53 | + month: null | |
| 54 | + }, | |
| 55 | + rules: { | |
| 56 | + year: [ | |
| 57 | + { required: true, message: '请选择年份', trigger: 'change' } | |
| 58 | + ], | |
| 59 | + month: [ | |
| 60 | + { required: true, message: '请选择月份', trigger: 'change' } | |
| 61 | + ] | |
| 62 | + }, | |
| 63 | + yearOptions: [], | |
| 64 | + monthOptions: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] | |
| 65 | + } | |
| 66 | + }, | |
| 67 | + methods: { | |
| 68 | + init() { | |
| 69 | + this.visible = true | |
| 70 | + this.initYearOptions() | |
| 71 | + this.resetForm() | |
| 72 | + }, | |
| 73 | + initYearOptions() { | |
| 74 | + const currentYear = new Date().getFullYear() | |
| 75 | + this.yearOptions = [] | |
| 76 | + for (let i = currentYear - 5; i <= currentYear + 1; i++) { | |
| 77 | + this.yearOptions.push(i) | |
| 78 | + } | |
| 79 | + }, | |
| 80 | + resetForm() { | |
| 81 | + this.form = { | |
| 82 | + year: null, | |
| 83 | + month: null | |
| 84 | + } | |
| 85 | + this.deleting = false | |
| 86 | + this.$nextTick(() => { | |
| 87 | + if (this.$refs.form) { | |
| 88 | + this.$refs.form.clearValidate() | |
| 89 | + } | |
| 90 | + }) | |
| 91 | + }, | |
| 92 | + confirmDelete() { | |
| 93 | + this.$refs.form.validate((valid) => { | |
| 94 | + if (valid) { | |
| 95 | + this.$confirm( | |
| 96 | + `确定要删除 ${this.form.year}年${this.form.month}月 的所有考勤数据吗?此操作不可恢复!`, | |
| 97 | + '确认删除', | |
| 98 | + { | |
| 99 | + confirmButtonText: '确定删除', | |
| 100 | + cancelButtonText: '取消', | |
| 101 | + type: 'warning' | |
| 102 | + } | |
| 103 | + ).then(() => { | |
| 104 | + this.performDelete() | |
| 105 | + }).catch(() => { | |
| 106 | + // 用户取消删除 | |
| 107 | + }) | |
| 108 | + } | |
| 109 | + }) | |
| 110 | + }, | |
| 111 | + performDelete() { | |
| 112 | + this.deleting = true | |
| 113 | + | |
| 114 | + request({ | |
| 115 | + url: `/api/Extend/lqattendancesummary/DeleteByMonth/${this.form.year}/${this.form.month}`, | |
| 116 | + method: 'DELETE' | |
| 117 | + }).then(res => { | |
| 118 | + this.deleting = false | |
| 119 | + if (res.code === 200) { | |
| 120 | + this.$message.success(res.msg || '删除成功') | |
| 121 | + this.close() | |
| 122 | + this.$emit('refresh', true) | |
| 123 | + } else { | |
| 124 | + this.$message.error(res.msg || '删除失败') | |
| 125 | + } | |
| 126 | + }).catch(err => { | |
| 127 | + this.deleting = false | |
| 128 | + this.$message.error('删除失败,请重试') | |
| 129 | + console.error('删除失败:', err) | |
| 130 | + }) | |
| 131 | + }, | |
| 132 | + close() { | |
| 133 | + this.visible = false | |
| 134 | + this.resetForm() | |
| 135 | + } | |
| 136 | + } | |
| 137 | +} | |
| 138 | +</script> | |
| 139 | + | |
| 140 | +<style scoped> | |
| 141 | +.dialog-footer { | |
| 142 | + text-align: right; | |
| 143 | +} | |
| 144 | +</style> | ... | ... |
antis-ncc-admin/src/views/attendance/ImportDialog.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog | |
| 3 | + title="导入考勤数据" | |
| 4 | + :visible.sync="visible" | |
| 5 | + width="500px" | |
| 6 | + :close-on-click-modal="false" | |
| 7 | + @close="close" | |
| 8 | + > | |
| 9 | + <div> | |
| 10 | + <el-form label-width="100px" label-position="right"> | |
| 11 | + <el-form-item label="选择文件"> | |
| 12 | + <el-upload | |
| 13 | + ref="upload" | |
| 14 | + :action="uploadUrl" | |
| 15 | + :headers="uploadHeaders" | |
| 16 | + :data="uploadData" | |
| 17 | + :file-list="fileList" | |
| 18 | + :before-upload="beforeUpload" | |
| 19 | + :on-success="onUploadSuccess" | |
| 20 | + :on-error="onUploadError" | |
| 21 | + :on-change="handleFileChange" | |
| 22 | + :auto-upload="false" | |
| 23 | + :limit="1" | |
| 24 | + :on-exceed="handleExceed" | |
| 25 | + accept=".xlsx,.xls" | |
| 26 | + drag | |
| 27 | + > | |
| 28 | + <i class="el-icon-upload"></i> | |
| 29 | + <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div> | |
| 30 | + <div class="el-upload__tip" slot="tip">只能上传一个xlsx/xls文件,且不超过10MB</div> | |
| 31 | + </el-upload> | |
| 32 | + </el-form-item> | |
| 33 | + </el-form> | |
| 34 | + | |
| 35 | + <div v-if="uploadResult" class="upload-result"> | |
| 36 | + <el-alert | |
| 37 | + :title="uploadResult.title" | |
| 38 | + :type="uploadResult.type" | |
| 39 | + :description="uploadResult.description" | |
| 40 | + show-icon | |
| 41 | + :closable="false" | |
| 42 | + /> | |
| 43 | + </div> | |
| 44 | + </div> | |
| 45 | + | |
| 46 | + <div slot="footer" class="dialog-footer"> | |
| 47 | + <el-button @click="close">取消</el-button> | |
| 48 | + <el-button type="primary" @click="submitUpload" :loading="uploading" > | |
| 49 | + 确定导入 | |
| 50 | + </el-button> | |
| 51 | + </div> | |
| 52 | + </el-dialog> | |
| 53 | +</template> | |
| 54 | + | |
| 55 | +<script> | |
| 56 | +import request from '@/utils/request' | |
| 57 | + | |
| 58 | +export default { | |
| 59 | + name: 'ImportDialog', | |
| 60 | + data() { | |
| 61 | + return { | |
| 62 | + visible: false, | |
| 63 | + uploading: false, | |
| 64 | + fileList: [], | |
| 65 | + uploadUrl: '', | |
| 66 | + uploadHeaders: {}, | |
| 67 | + uploadData: {}, | |
| 68 | + uploadResult: null | |
| 69 | + } | |
| 70 | + }, | |
| 71 | + methods: { | |
| 72 | + init() { | |
| 73 | + this.visible = true | |
| 74 | + this.resetForm() | |
| 75 | + }, | |
| 76 | + resetForm() { | |
| 77 | + this.fileList = [] | |
| 78 | + this.uploadResult = null | |
| 79 | + this.uploading = false | |
| 80 | + }, | |
| 81 | + beforeUpload(file) { | |
| 82 | + const isExcel = file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' || | |
| 83 | + file.type === 'application/vnd.ms-excel' | |
| 84 | + const isLt10M = file.size / 1024 / 1024 < 10 | |
| 85 | + | |
| 86 | + if (!isExcel) { | |
| 87 | + this.$message.error('只能上传Excel文件!') | |
| 88 | + return false | |
| 89 | + } | |
| 90 | + if (!isLt10M) { | |
| 91 | + this.$message.error('上传文件大小不能超过 10MB!') | |
| 92 | + return false | |
| 93 | + } | |
| 94 | + return true | |
| 95 | + }, | |
| 96 | + submitUpload() { | |
| 97 | + console.log('fileList:', this.fileList) | |
| 98 | + console.log('fileList.length:', this.fileList.length) | |
| 99 | + | |
| 100 | + if (!this.fileList.length || !this.fileList[0]) { | |
| 101 | + this.$message.warning('请选择要上传的文件') | |
| 102 | + return | |
| 103 | + } | |
| 104 | + | |
| 105 | + const file = this.fileList[0] | |
| 106 | + console.log('selected file:', file) | |
| 107 | + | |
| 108 | + if (!file.raw) { | |
| 109 | + this.$message.warning('文件信息异常,请重新选择文件') | |
| 110 | + return | |
| 111 | + } | |
| 112 | + | |
| 113 | + this.uploading = true | |
| 114 | + this.uploadResult = null | |
| 115 | + | |
| 116 | + const formData = new FormData() | |
| 117 | + formData.append('file', file.raw) | |
| 118 | + | |
| 119 | + request({ | |
| 120 | + url: '/api/Extend/lqattendancesummary/ImportAttendanceDataFromExcel', | |
| 121 | + method: 'POST', | |
| 122 | + data: formData, | |
| 123 | + headers: { | |
| 124 | + 'Content-Type': 'multipart/form-data' | |
| 125 | + } | |
| 126 | + }).then(res => { | |
| 127 | + this.uploading = false | |
| 128 | + if (res.code === 200) { | |
| 129 | + this.uploadResult = { | |
| 130 | + type: 'success', | |
| 131 | + title: '导入成功', | |
| 132 | + description: res.msg || '数据导入完成' | |
| 133 | + } | |
| 134 | + this.$message.success('导入成功') | |
| 135 | + setTimeout(() => { | |
| 136 | + this.close() | |
| 137 | + this.$emit('refresh', true) | |
| 138 | + }, 2000) | |
| 139 | + } else { | |
| 140 | + this.uploadResult = { | |
| 141 | + type: 'error', | |
| 142 | + title: '导入失败', | |
| 143 | + description: res.msg || '数据导入失败,请检查文件格式' | |
| 144 | + } | |
| 145 | + } | |
| 146 | + }).catch(err => { | |
| 147 | + this.uploading = false | |
| 148 | + this.uploadResult = { | |
| 149 | + type: 'error', | |
| 150 | + title: '导入失败', | |
| 151 | + description: err.message || '网络错误,请重试' | |
| 152 | + } | |
| 153 | + this.$message.error('导入失败') | |
| 154 | + }) | |
| 155 | + }, | |
| 156 | + onUploadSuccess(response, file, fileList) { | |
| 157 | + // 这里不需要处理,因为我们使用手动上传 | |
| 158 | + }, | |
| 159 | + onUploadError(err, file, fileList) { | |
| 160 | + this.uploading = false | |
| 161 | + this.uploadResult = { | |
| 162 | + type: 'error', | |
| 163 | + title: '上传失败', | |
| 164 | + description: '文件上传失败,请重试' | |
| 165 | + } | |
| 166 | + }, | |
| 167 | + handleExceed(files, fileList) { | |
| 168 | + this.$message.warning('只能上传一个文件,请先删除已选择的文件') | |
| 169 | + }, | |
| 170 | + handleFileChange(file, fileList) { | |
| 171 | + console.log('文件变化:', file, fileList) | |
| 172 | + this.fileList = fileList | |
| 173 | + }, | |
| 174 | + close() { | |
| 175 | + this.visible = false | |
| 176 | + this.resetForm() | |
| 177 | + } | |
| 178 | + } | |
| 179 | +} | |
| 180 | +</script> | |
| 181 | + | |
| 182 | +<style scoped> | |
| 183 | +.upload-result { | |
| 184 | + margin-top: 20px; | |
| 185 | +} | |
| 186 | + | |
| 187 | +.el-upload-dragger { | |
| 188 | + width: 100%; | |
| 189 | +} | |
| 190 | +</style> | ... | ... |
antis-ncc-admin/src/views/attendance/index.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="NCC-common-layout"> | |
| 3 | + <div class="NCC-common-layout-center"> | |
| 4 | + <el-row class="NCC-common-search-box" :gutter="16"> | |
| 5 | + <el-form @submit.native.prevent> | |
| 6 | + <el-col :span="6"> | |
| 7 | + <el-form-item label="员工姓名"> | |
| 8 | + <el-input v-model="query.userName" placeholder="员工姓名" clearable /> | |
| 9 | + </el-form-item> | |
| 10 | + </el-col> | |
| 11 | + <el-col :span="6"> | |
| 12 | + <el-form-item label="年份"> | |
| 13 | + <el-select v-model="query.year" placeholder="年份" clearable> | |
| 14 | + <el-option v-for="year in yearOptions" :key="year" :label="year + '年'" :value="year" /> | |
| 15 | + </el-select> | |
| 16 | + </el-form-item> | |
| 17 | + </el-col> | |
| 18 | + <el-col :span="6"> | |
| 19 | + <el-form-item label="月份"> | |
| 20 | + <el-select v-model="query.month" placeholder="月份" clearable> | |
| 21 | + <el-option v-for="month in monthOptions" :key="month" :label="month + '月'" :value="month" /> | |
| 22 | + </el-select> | |
| 23 | + </el-form-item> | |
| 24 | + </el-col> | |
| 25 | + <el-col :span="6"> | |
| 26 | + <el-form-item label="员工状态"> | |
| 27 | + <el-select v-model="query.employeeStatus" placeholder="员工状态" clearable> | |
| 28 | + <el-option label="在职" :value="1" /> | |
| 29 | + <el-option label="离职" :value="0" /> | |
| 30 | + </el-select> | |
| 31 | + </el-form-item> | |
| 32 | + </el-col> | |
| 33 | + <el-col :span="6"> | |
| 34 | + <el-form-item> | |
| 35 | + <el-button type="primary" icon="el-icon-search" @click="search()">查询</el-button> | |
| 36 | + <el-button icon="el-icon-refresh-right" @click="reset()">重置</el-button> | |
| 37 | + </el-form-item> | |
| 38 | + </el-col> | |
| 39 | + </el-form> | |
| 40 | + </el-row> | |
| 41 | + <div class="NCC-common-layout-main NCC-flex-main"> | |
| 42 | + <div class="NCC-common-head"> | |
| 43 | + <div> | |
| 44 | + <el-button type="primary" icon="el-icon-upload2" @click="handleImport()">导入</el-button> | |
| 45 | + <el-button type="text" icon="el-icon-download" @click="downloadTemplate()">导入模板</el-button> | |
| 46 | + <el-button type="text" icon="el-icon-delete" @click="handleBatchDelete()">批量删除</el-button> | |
| 47 | + </div> | |
| 48 | + <div class="NCC-common-head-right"> | |
| 49 | + <el-tooltip effect="dark" content="刷新" placement="top"> | |
| 50 | + <el-link icon="icon-ym icon-ym-Refresh NCC-common-head-icon" :underline="false" @click="reset()" /> | |
| 51 | + </el-tooltip> | |
| 52 | + <screenfull isContainer /> | |
| 53 | + </div> | |
| 54 | + </div> | |
| 55 | + <NCC-table v-loading="listLoading" :data="list" has-c @selection-change="handleSelectionChange"> | |
| 56 | + <el-table-column prop="userName" label="员工姓名" align="left" /> | |
| 57 | + <el-table-column prop="year" label="年份" align="left" /> | |
| 58 | + <el-table-column prop="month" label="月份" align="left" /> | |
| 59 | + <el-table-column label="员工状态" prop="employeeStatus" align="left"> | |
| 60 | + <template slot-scope="scope"> | |
| 61 | + <el-tag :type="scope.row.employeeStatus === 1 ? 'success' : 'danger'" size="small"> | |
| 62 | + {{ scope.row.employeeStatus === 1 ? '在职' : '离职' }} | |
| 63 | + </el-tag> | |
| 64 | + </template> | |
| 65 | + </el-table-column> | |
| 66 | + <el-table-column prop="workDays" label="工作天数" align="left" /> | |
| 67 | + <el-table-column prop="leaveDays" label="请假天数" align="left" /> | |
| 68 | + <el-table-column prop="restDays" label="休息天数" align="left" /> | |
| 69 | + <el-table-column prop="remark" label="备注" align="left" /> | |
| 70 | + <el-table-column label="创建时间" prop="createTime" align="left" width="150"> | |
| 71 | + <template slot-scope="scope"> | |
| 72 | + {{ formatTime(scope.row.createTime) }} | |
| 73 | + </template> | |
| 74 | + </el-table-column> | |
| 75 | + <el-table-column label="更新时间" prop="updateTime" align="left" width="150"> | |
| 76 | + <template slot-scope="scope"> | |
| 77 | + {{ formatTime(scope.row.updateTime) }} | |
| 78 | + </template> | |
| 79 | + </el-table-column> | |
| 80 | + </NCC-table> | |
| 81 | + <pagination :total="total" :page.sync="listQuery.currentPage" :limit.sync="listQuery.pageSize" @pagination="initData" /> | |
| 82 | + </div> | |
| 83 | + </div> | |
| 84 | + | |
| 85 | + <!-- 导入弹窗 --> | |
| 86 | + <ImportDialog v-if="importVisible" ref="ImportDialog" @refresh="refresh" /> | |
| 87 | + | |
| 88 | + <!-- 批量删除弹窗 --> | |
| 89 | + <BatchDeleteDialog v-if="batchDeleteVisible" ref="BatchDeleteDialog" @refresh="refresh" /> | |
| 90 | + </div> | |
| 91 | +</template> | |
| 92 | + | |
| 93 | +<script> | |
| 94 | + import request from '@/utils/request' | |
| 95 | + import ImportDialog from './ImportDialog' | |
| 96 | + import BatchDeleteDialog from './BatchDeleteDialog' | |
| 97 | + | |
| 98 | + export default { | |
| 99 | + components: { ImportDialog, BatchDeleteDialog }, | |
| 100 | + data() { | |
| 101 | + return { | |
| 102 | + query: { | |
| 103 | + userName: undefined, | |
| 104 | + year: undefined, | |
| 105 | + month: undefined, | |
| 106 | + employeeStatus: undefined, | |
| 107 | + }, | |
| 108 | + list: [], | |
| 109 | + listLoading: true, | |
| 110 | + multipleSelection: [], | |
| 111 | + total: 0, | |
| 112 | + listQuery: { | |
| 113 | + currentPage: 1, | |
| 114 | + pageSize: 20, | |
| 115 | + sort: "desc", | |
| 116 | + sidx: "", | |
| 117 | + }, | |
| 118 | + importVisible: false, | |
| 119 | + batchDeleteVisible: false, | |
| 120 | + yearOptions: [], | |
| 121 | + monthOptions: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], | |
| 122 | + } | |
| 123 | + }, | |
| 124 | + computed: {}, | |
| 125 | + created() { | |
| 126 | + this.initYearOptions() | |
| 127 | + this.initData() | |
| 128 | + }, | |
| 129 | + methods: { | |
| 130 | + initYearOptions() { | |
| 131 | + const currentYear = new Date().getFullYear() | |
| 132 | + for (let i = currentYear - 5; i <= currentYear + 1; i++) { | |
| 133 | + this.yearOptions.push(i) | |
| 134 | + } | |
| 135 | + }, | |
| 136 | + initData() { | |
| 137 | + this.listLoading = true; | |
| 138 | + let _query = { | |
| 139 | + ...this.listQuery, | |
| 140 | + ...this.query | |
| 141 | + }; | |
| 142 | + let query = {} | |
| 143 | + for (let key in _query) { | |
| 144 | + if (Array.isArray(_query[key])) { | |
| 145 | + query[key] = _query[key].join() | |
| 146 | + } else { | |
| 147 | + query[key] = _query[key] | |
| 148 | + } | |
| 149 | + } | |
| 150 | + request({ | |
| 151 | + url: `/api/Extend/lqattendancesummary`, | |
| 152 | + method: 'GET', | |
| 153 | + data: query | |
| 154 | + }).then(res => { | |
| 155 | + this.list = res.data.list | |
| 156 | + this.total = res.data.pagination.total | |
| 157 | + this.listLoading = false | |
| 158 | + }) | |
| 159 | + }, | |
| 160 | + handleDel(id) { | |
| 161 | + this.$confirm('此操作将永久删除该数据, 是否继续?', '提示', { | |
| 162 | + type: 'warning' | |
| 163 | + }).then(() => { | |
| 164 | + request({ | |
| 165 | + url: `/api/Extend/lqattendancesummary/${id}`, | |
| 166 | + method: 'DELETE' | |
| 167 | + }).then(res => { | |
| 168 | + this.$message({ | |
| 169 | + type: 'success', | |
| 170 | + message: res.msg, | |
| 171 | + onClose: () => { | |
| 172 | + this.initData() | |
| 173 | + } | |
| 174 | + }); | |
| 175 | + }) | |
| 176 | + }).catch(() => { | |
| 177 | + }); | |
| 178 | + }, | |
| 179 | + handleSelectionChange(val) { | |
| 180 | + const res = val.map(item => item.id) | |
| 181 | + this.multipleSelection = res | |
| 182 | + }, | |
| 183 | + handleImport() { | |
| 184 | + this.importVisible = true | |
| 185 | + this.$nextTick(() => { | |
| 186 | + this.$refs.ImportDialog.init() | |
| 187 | + }) | |
| 188 | + }, | |
| 189 | + handleBatchDelete() { | |
| 190 | + this.batchDeleteVisible = true | |
| 191 | + this.$nextTick(() => { | |
| 192 | + this.$refs.BatchDeleteDialog.init() | |
| 193 | + }) | |
| 194 | + }, | |
| 195 | + downloadTemplate() { | |
| 196 | + window.open('https://erp.lvqianmeiye.com/FileTemplate/考勤统计导入模板.xlsx', '_blank') | |
| 197 | + }, | |
| 198 | + formatTime(timestamp) { | |
| 199 | + if (!timestamp) return '无' | |
| 200 | + const date = new Date(timestamp) | |
| 201 | + return date.toLocaleString('zh-CN', { | |
| 202 | + year: 'numeric', | |
| 203 | + month: '2-digit', | |
| 204 | + day: '2-digit', | |
| 205 | + hour: '2-digit', | |
| 206 | + minute: '2-digit', | |
| 207 | + second: '2-digit' | |
| 208 | + }) | |
| 209 | + }, | |
| 210 | + search() { | |
| 211 | + this.listQuery = { | |
| 212 | + currentPage: 1, | |
| 213 | + pageSize: 20, | |
| 214 | + sort: "desc", | |
| 215 | + sidx: "", | |
| 216 | + } | |
| 217 | + this.initData() | |
| 218 | + }, | |
| 219 | + refresh(isrRefresh) { | |
| 220 | + this.importVisible = false | |
| 221 | + this.batchDeleteVisible = false | |
| 222 | + if (isrRefresh) this.reset() | |
| 223 | + }, | |
| 224 | + reset() { | |
| 225 | + for (let key in this.query) { | |
| 226 | + this.query[key] = undefined | |
| 227 | + } | |
| 228 | + this.listQuery = { | |
| 229 | + currentPage: 1, | |
| 230 | + pageSize: 20, | |
| 231 | + sort: "desc", | |
| 232 | + sidx: "", | |
| 233 | + } | |
| 234 | + this.initData() | |
| 235 | + } | |
| 236 | + } | |
| 237 | + } | |
| 238 | +</script> | ... | ... |
antis-ncc-admin/src/views/lqMdxx/Form.vue
| ... | ... | @@ -94,6 +94,20 @@ |
| 94 | 94 | </el-select> |
| 95 | 95 | </el-form-item> |
| 96 | 96 | </el-col> |
| 97 | + <el-col :span="24"> | |
| 98 | + <el-form-item label="门店类别" prop="storeCategory"> | |
| 99 | + <el-select v-model="dataForm.storeCategory" placeholder="请选择" clearable :style='{"width":"100%"}' > | |
| 100 | + <el-option v-for="(item, index) in storeCategoryOptions" :key="index" :label="item.Name" :value="item.Value" ></el-option> | |
| 101 | + </el-select> | |
| 102 | + </el-form-item> | |
| 103 | + </el-col> | |
| 104 | + <el-col :span="24"> | |
| 105 | + <el-form-item label="门店类型" prop="storeType"> | |
| 106 | + <el-select v-model="dataForm.storeType" placeholder="请选择" clearable :style='{"width":"100%"}' > | |
| 107 | + <el-option v-for="(item, index) in storeTypeOptions" :key="index" :label="item.Name" :value="item.Value" ></el-option> | |
| 108 | + </el-select> | |
| 109 | + </el-form-item> | |
| 110 | + </el-col> | |
| 97 | 111 | </el-form> |
| 98 | 112 | </el-row> |
| 99 | 113 | <span slot="footer" class="dialog-footer"> |
| ... | ... | @@ -131,16 +145,22 @@ |
| 131 | 145 | gsmc:undefined, |
| 132 | 146 | fr:undefined, |
| 133 | 147 | ywsb:undefined, |
| 148 | + storeCategory:undefined, | |
| 149 | + storeType:undefined, | |
| 134 | 150 | }, |
| 135 | 151 | rules: { |
| 136 | 152 | }, |
| 137 | 153 | zxztOptions:[{"fullName":"开店","id":"开店"},{"fullName":"闭店","id":"闭店"}], |
| 138 | 154 | ywsbOptions:[{"fullName":"有","id":"有"},{"fullName":"无","id":"无"}], |
| 155 | + storeCategoryOptions:[], | |
| 156 | + storeTypeOptions:[], | |
| 139 | 157 | } |
| 140 | 158 | }, |
| 141 | 159 | computed: {}, |
| 142 | 160 | watch: {}, |
| 143 | 161 | created() { |
| 162 | + this.loadStoreCategoryOptions(); | |
| 163 | + this.loadStoreTypeOptions(); | |
| 144 | 164 | }, |
| 145 | 165 | mounted() { |
| 146 | 166 | }, |
| ... | ... | @@ -148,6 +168,30 @@ |
| 148 | 168 | goBack() { |
| 149 | 169 | this.$emit('refresh') |
| 150 | 170 | }, |
| 171 | + // 加载门店类别选项 | |
| 172 | + loadStoreCategoryOptions() { | |
| 173 | + request({ | |
| 174 | + url: '/api/Extend/lqmdxx/Selector/StoreCategory', | |
| 175 | + method: 'get' | |
| 176 | + }).then(res => { | |
| 177 | + this.storeCategoryOptions = res.data || []; | |
| 178 | + }).catch(err => { | |
| 179 | + console.error('加载门店类别选项失败:', err); | |
| 180 | + this.storeCategoryOptions = []; | |
| 181 | + }); | |
| 182 | + }, | |
| 183 | + // 加载门店类型选项 | |
| 184 | + loadStoreTypeOptions() { | |
| 185 | + request({ | |
| 186 | + url: '/api/Extend/lqmdxx/Selector/StoreType', | |
| 187 | + method: 'get' | |
| 188 | + }).then(res => { | |
| 189 | + this.storeTypeOptions = res.data || []; | |
| 190 | + }).catch(err => { | |
| 191 | + console.error('加载门店类型选项失败:', err); | |
| 192 | + this.storeTypeOptions = []; | |
| 193 | + }); | |
| 194 | + }, | |
| 151 | 195 | init(id, isDetail) { |
| 152 | 196 | this.dataForm.id = id || 0; |
| 153 | 197 | this.visible = true; | ... | ... |
antis-ncc-admin/src/views/lqMdxx/SetNewStoreDialog.vue
| ... | ... | @@ -31,6 +31,13 @@ |
| 31 | 31 | style="width: 100%"> |
| 32 | 32 | </el-date-picker> |
| 33 | 33 | </el-form-item> |
| 34 | + <el-form-item label="阶段" prop="stage"> | |
| 35 | + <el-select v-model="form.stage" placeholder="请选择阶段" style="width: 100%" clearable> | |
| 36 | + <el-option label="第一阶段" :value="1"></el-option> | |
| 37 | + <el-option label="第二阶段" :value="2"></el-option> | |
| 38 | + <el-option label="第三阶段" :value="3"></el-option> | |
| 39 | + </el-select> | |
| 40 | + </el-form-item> | |
| 34 | 41 | <el-form-item label="说明" prop="sm"> |
| 35 | 42 | <el-input |
| 36 | 43 | v-model="form.sm" |
| ... | ... | @@ -69,7 +76,8 @@ export default { |
| 69 | 76 | bhkssj: '', |
| 70 | 77 | bhjssj: '', |
| 71 | 78 | sm: '', |
| 72 | - sfqy: 1 | |
| 79 | + sfqy: 1, | |
| 80 | + stage: null | |
| 73 | 81 | }, |
| 74 | 82 | rules: { |
| 75 | 83 | mdid: [ |
| ... | ... | @@ -81,6 +89,9 @@ export default { |
| 81 | 89 | bhjssj: [ |
| 82 | 90 | { required: true, message: '请选择保护结束时间', trigger: 'change' } |
| 83 | 91 | ], |
| 92 | + stage: [ | |
| 93 | + { required: true, message: '请选择阶段', trigger: 'change' } | |
| 94 | + ], | |
| 84 | 95 | sfqy: [ |
| 85 | 96 | { required: true, message: '请选择是否启用', trigger: 'change' } |
| 86 | 97 | ] |
| ... | ... | @@ -153,12 +164,13 @@ export default { |
| 153 | 164 | method: 'POST', |
| 154 | 165 | data: submitData |
| 155 | 166 | }).then(res => { |
| 156 | - this.$message.success('设置成功') | |
| 157 | - this.handleClose() | |
| 158 | - this.$emit('refresh') | |
| 159 | - }).catch(err => { | |
| 160 | - this.$message.error('设置失败:' + (err.msg || '未知错误')) | |
| 161 | - console.error(err) | |
| 167 | + if(res.code == 200) { | |
| 168 | + this.$message.success('设置成功') | |
| 169 | + this.handleClose() | |
| 170 | + this.$emit('refresh') | |
| 171 | + } else { | |
| 172 | + this.$message.error(res.msg) | |
| 173 | + } | |
| 162 | 174 | }).finally(() => { |
| 163 | 175 | this.submitLoading = false |
| 164 | 176 | }) | ... | ... |
antis-ncc-admin/src/views/lqMdxx/ViewNewStoreDialog.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog | |
| 3 | + title="查看新店设置" | |
| 4 | + :visible.sync="visible" | |
| 5 | + width="800px" | |
| 6 | + :close-on-click-modal="false" | |
| 7 | + @close="close" | |
| 8 | + > | |
| 9 | + <div v-loading="loading"> | |
| 10 | + <el-table :data="list" border stripe> | |
| 11 | + <el-table-column label="保护开始时间" width="150"> | |
| 12 | + <template slot-scope="scope"> | |
| 13 | + {{ formatTime(scope.row.bhkssj) }} | |
| 14 | + </template> | |
| 15 | + </el-table-column> | |
| 16 | + <el-table-column label="保护结束时间" width="150"> | |
| 17 | + <template slot-scope="scope"> | |
| 18 | + {{ formatTime(scope.row.bhjssj) }} | |
| 19 | + </template> | |
| 20 | + </el-table-column> | |
| 21 | + <el-table-column prop="sm" label="说明" /> | |
| 22 | + <el-table-column label="创建时间" width="150"> | |
| 23 | + <template slot-scope="scope"> | |
| 24 | + {{ formatTime(scope.row.cjsj) }} | |
| 25 | + </template> | |
| 26 | + </el-table-column> | |
| 27 | + <el-table-column label="是否启用" width="100"> | |
| 28 | + <template slot-scope="scope"> | |
| 29 | + <el-tag :type="scope.row.sfqy === 1 ? 'success' : 'danger'" size="small"> | |
| 30 | + {{ scope.row.sfqy === 1 ? '启用' : '禁用' }} | |
| 31 | + </el-tag> | |
| 32 | + </template> | |
| 33 | + </el-table-column> | |
| 34 | + <el-table-column label="阶段" width="80"> | |
| 35 | + <template slot-scope="scope"> | |
| 36 | + {{ scope.row.stage==1 ? '第一阶段' : scope.row.stage==2 ? '第二阶段' : scope.row.stage == 3 ? '第三阶段' : '无' }} | |
| 37 | + </template> | |
| 38 | + </el-table-column> | |
| 39 | + </el-table> | |
| 40 | + | |
| 41 | + <div v-if="list.length === 0" class="empty-data"> | |
| 42 | + <i class="el-icon-info"></i> | |
| 43 | + <p>暂无新店设置数据</p> | |
| 44 | + </div> | |
| 45 | + </div> | |
| 46 | + | |
| 47 | + <div slot="footer" class="dialog-footer"> | |
| 48 | + <el-button @click="close">关闭</el-button> | |
| 49 | + </div> | |
| 50 | + </el-dialog> | |
| 51 | +</template> | |
| 52 | + | |
| 53 | +<script> | |
| 54 | +import request from '@/utils/request' | |
| 55 | + | |
| 56 | +export default { | |
| 57 | + name: 'ViewNewStoreDialog', | |
| 58 | + data() { | |
| 59 | + return { | |
| 60 | + visible: false, | |
| 61 | + loading: false, | |
| 62 | + list: [], | |
| 63 | + storeId: null | |
| 64 | + } | |
| 65 | + }, | |
| 66 | + methods: { | |
| 67 | + init(storeId) { | |
| 68 | + this.storeId = storeId | |
| 69 | + this.visible = true | |
| 70 | + this.loadData() | |
| 71 | + }, | |
| 72 | + loadData() { | |
| 73 | + this.loading = true | |
| 74 | + request({ | |
| 75 | + url: '/api/Extend/lqmdxdbhsj', | |
| 76 | + method: 'GET', | |
| 77 | + data: { | |
| 78 | + mdid: this.storeId | |
| 79 | + } | |
| 80 | + }).then(res => { | |
| 81 | + this.list = res.data.list || [] | |
| 82 | + this.loading = false | |
| 83 | + }).catch(err => { | |
| 84 | + this.$message.error('获取数据失败') | |
| 85 | + this.loading = false | |
| 86 | + }) | |
| 87 | + }, | |
| 88 | + formatTime(timestamp) { | |
| 89 | + if (!timestamp) return '无' | |
| 90 | + const date = new Date(timestamp) | |
| 91 | + return date.toLocaleString('zh-CN', { | |
| 92 | + year: 'numeric', | |
| 93 | + month: '2-digit', | |
| 94 | + day: '2-digit', | |
| 95 | + hour: '2-digit', | |
| 96 | + minute: '2-digit', | |
| 97 | + second: '2-digit' | |
| 98 | + }) | |
| 99 | + }, | |
| 100 | + close() { | |
| 101 | + this.visible = false | |
| 102 | + this.list = [] | |
| 103 | + this.storeId = null | |
| 104 | + } | |
| 105 | + } | |
| 106 | +} | |
| 107 | +</script> | |
| 108 | + | |
| 109 | +<style scoped> | |
| 110 | +.empty-data { | |
| 111 | + text-align: center; | |
| 112 | + padding: 40px 0; | |
| 113 | + color: #909399; | |
| 114 | +} | |
| 115 | + | |
| 116 | +.empty-data i { | |
| 117 | + font-size: 48px; | |
| 118 | + margin-bottom: 16px; | |
| 119 | + display: block; | |
| 120 | +} | |
| 121 | + | |
| 122 | +.empty-data p { | |
| 123 | + margin: 0; | |
| 124 | + font-size: 14px; | |
| 125 | +} | |
| 126 | +</style> | ... | ... |
antis-ncc-admin/src/views/lqMdxx/index.vue
| ... | ... | @@ -3,7 +3,7 @@ |
| 3 | 3 | <div class="NCC-common-layout-center"> |
| 4 | 4 | <el-row class="NCC-common-search-box" :gutter="16"> |
| 5 | 5 | <el-form @submit.native.prevent> |
| 6 | - <el-col :span="6"> | |
| 6 | + <!-- <el-col :span="6"> | |
| 7 | 7 | <el-form-item label="主键"> |
| 8 | 8 | <el-input v-model="query.id" placeholder="主键" clearable /> |
| 9 | 9 | </el-form-item> |
| ... | ... | @@ -17,9 +17,8 @@ |
| 17 | 17 | <el-form-item label="单据门店编号"> |
| 18 | 18 | <el-input v-model="query.djmdbh" placeholder="单据门店编号" clearable /> |
| 19 | 19 | </el-form-item> |
| 20 | - </el-col> | |
| 21 | - <template v-if="showAll"> | |
| 22 | - <el-col :span="6"> | |
| 20 | + </el-col> --> | |
| 21 | + <el-col :span="6"> | |
| 23 | 22 | <el-form-item label="单据门店"> |
| 24 | 23 | <el-input v-model="query.djmd" placeholder="单据门店" clearable /> |
| 25 | 24 | </el-form-item> |
| ... | ... | @@ -34,11 +33,13 @@ |
| 34 | 33 | <el-input v-model="query.cs" placeholder="城市" clearable /> |
| 35 | 34 | </el-form-item> |
| 36 | 35 | </el-col> |
| 37 | - <el-col :span="6"> | |
| 36 | + <template v-if="showAll"> | |
| 37 | + | |
| 38 | + <!-- <el-col :span="6"> | |
| 38 | 39 | <el-form-item label="地址"> |
| 39 | 40 | <el-input v-model="query.dz" placeholder="地址" clearable /> |
| 40 | 41 | </el-form-item> |
| 41 | - </el-col> | |
| 42 | + </el-col> --> | |
| 42 | 43 | <el-col :span="6"> |
| 43 | 44 | <el-form-item label="姓名"> |
| 44 | 45 | <el-input v-model="query.xm" placeholder="姓名" clearable /> |
| ... | ... | @@ -110,34 +111,40 @@ |
| 110 | 111 | </div> |
| 111 | 112 | </div> |
| 112 | 113 | <NCC-table v-loading="listLoading" :data="list" has-c @selection-change="handleSelectionChange"> |
| 113 | - <el-table-column prop="id" label="主键" align="left" /> | |
| 114 | - <el-table-column prop="mdbm" label="门店编码" align="left" /> | |
| 115 | - <el-table-column prop="djmdbh" label="单据门店编号" align="left" /> | |
| 114 | + <!-- <el-table-column prop="id" label="主键" align="left" /> --> | |
| 115 | + <!-- <el-table-column prop="mdbm" label="门店编码" align="left" /> | |
| 116 | + <el-table-column prop="djmdbh" label="门店编号" align="left" /> --> | |
| 116 | 117 | <el-table-column prop="djmd" label="单据门店" align="left" /> |
| 117 | 118 | <el-table-column prop="dm" label="店名" align="left" /> |
| 118 | 119 | <el-table-column prop="cs" label="城市" align="left" /> |
| 119 | - <el-table-column prop="dz" label="地址" align="left" /> | |
| 120 | + <!-- <el-table-column prop="dz" label="地址" align="left" /> --> | |
| 120 | 121 | <el-table-column prop="xm" label="姓名" align="left" /> |
| 121 | 122 | <el-table-column prop="dhhm" label="电话号码" align="left" /> |
| 122 | 123 | <el-table-column prop="zj" label="座机" align="left" /> |
| 123 | - <el-table-column prop="kysj" label="开业时间" align="left" /> | |
| 124 | + <el-table-column prop="kysj" width="150" label="开业时间" align="left" :formatter="ncc.tableDateFormat"/> | |
| 124 | 125 | <el-table-column label="最新状态" prop="zxzt" align="left"> |
| 125 | 126 | <template slot-scope="scope">{{ scope.row.zxzt | dynamicText(zxztOptions) }}</template> |
| 126 | 127 | </el-table-column> |
| 127 | 128 | <el-table-column prop="gsmc" label="工商名称" align="left" /> |
| 128 | 129 | <el-table-column prop="fr" label="法人" align="left" /> |
| 129 | - <el-table-column label="有无社保" prop="ywsb" align="left"> | |
| 130 | + <!-- <el-table-column label="有无社保" prop="ywsb" align="left"> | |
| 130 | 131 | <template slot-scope="scope">{{ scope.row.ywsb | dynamicText(ywsbOptions) }}</template> |
| 131 | - </el-table-column> | |
| 132 | + </el-table-column> --> | |
| 132 | 133 | <el-table-column label="是否新店" prop="isNewStore" align="left"> |
| 133 | 134 | <template slot-scope="scope"> |
| 134 | 135 | <el-tag v-if="scope.row.isNewStore" type="success" size="small">新店</el-tag> |
| 135 | 136 | <el-tag v-else type="info" size="small">老店</el-tag> |
| 136 | 137 | </template> |
| 137 | 138 | </el-table-column> |
| 139 | + <el-table-column label="阶段" prop="stage" align="left"> | |
| 140 | + <template slot-scope="scope"> | |
| 141 | + {{ scope.row.stage==1 ? '第一阶段' : scope.row.stage==2 ? '第二阶段' : scope.row.stage == 3 ? '第三阶段' : '无' }} | |
| 142 | + </template> | |
| 143 | + </el-table-column> | |
| 138 | 144 | <el-table-column label="操作" fixed="right" width="150"> |
| 139 | 145 | <template slot-scope="scope"> |
| 140 | 146 | <el-button type="text" @click="addOrUpdateHandle(scope.row.id)" >编辑</el-button> |
| 147 | + <el-button type="text" @click="viewNewStoreHandle(scope.row)">查看新店设置</el-button> | |
| 141 | 148 | <el-button type="text" @click="setNewStoreHandle(scope.row)" >设置新店</el-button> |
| 142 | 149 | <el-button type="text" @click="handleDel(scope.row.id)" class="NCC-table-delBtn" >删除</el-button> |
| 143 | 150 | </template> |
| ... | ... | @@ -149,6 +156,7 @@ |
| 149 | 156 | <NCC-Form v-if="formVisible" ref="NCCForm" @refresh="refresh" /> |
| 150 | 157 | <ExportBox v-if="exportBoxVisible" ref="ExportBox" @download="download" /> |
| 151 | 158 | <SetNewStoreDialog v-if="setNewStoreVisible" ref="SetNewStoreDialog" @refresh="refresh" /> |
| 159 | + <ViewNewStoreDialog v-if="viewNewStoreVisible" ref="ViewNewStoreDialog" /> | |
| 152 | 160 | </div> |
| 153 | 161 | </template> |
| 154 | 162 | <script> |
| ... | ... | @@ -157,9 +165,10 @@ |
| 157 | 165 | import NCCForm from './Form' |
| 158 | 166 | import ExportBox from './ExportBox' |
| 159 | 167 | import SetNewStoreDialog from './SetNewStoreDialog' |
| 168 | + import ViewNewStoreDialog from './ViewNewStoreDialog' | |
| 160 | 169 | import { previewDataInterface } from '@/api/systemData/dataInterface' |
| 161 | 170 | export default { |
| 162 | - components: { NCCForm, ExportBox, SetNewStoreDialog }, | |
| 171 | + components: { NCCForm, ExportBox, SetNewStoreDialog, ViewNewStoreDialog }, | |
| 163 | 172 | data() { |
| 164 | 173 | return { |
| 165 | 174 | showAll: false, |
| ... | ... | @@ -192,6 +201,7 @@ |
| 192 | 201 | formVisible: false, |
| 193 | 202 | exportBoxVisible: false, |
| 194 | 203 | setNewStoreVisible: false, |
| 204 | + viewNewStoreVisible: false, | |
| 195 | 205 | columnList: [ |
| 196 | 206 | { prop: 'id', label: '主键' }, |
| 197 | 207 | { prop: 'mdbm', label: '门店编码' }, |
| ... | ... | @@ -314,6 +324,12 @@ |
| 314 | 324 | this.$refs.SetNewStoreDialog.init(storeData) |
| 315 | 325 | }) |
| 316 | 326 | }, |
| 327 | + viewNewStoreHandle(storeData) { | |
| 328 | + this.viewNewStoreVisible = true | |
| 329 | + this.$nextTick(() => { | |
| 330 | + this.$refs.ViewNewStoreDialog.init(storeData.id) | |
| 331 | + }) | |
| 332 | + }, | |
| 317 | 333 | checkNewStoreStatus() { |
| 318 | 334 | // 为每个门店检查是否在新店保护期内 |
| 319 | 335 | this.list.forEach(store => { |
| ... | ... | @@ -322,8 +338,10 @@ |
| 322 | 338 | method: 'GET' |
| 323 | 339 | }).then(res => { |
| 324 | 340 | this.$set(store, 'isNewStore', res.data.hasProtection) |
| 341 | + this.$set(store, 'stage', res.data.data.stage) | |
| 325 | 342 | }).catch(err => { |
| 326 | 343 | this.$set(store, 'isNewStore', false) |
| 344 | + this.$set(store, 'stage', 0) | |
| 327 | 345 | }) |
| 328 | 346 | }) |
| 329 | 347 | }, | ... | ... |
antis-ncc-admin/src/views/lqXmzl/Form.vue
| ... | ... | @@ -168,6 +168,18 @@ |
| 168 | 168 | </el-select> |
| 169 | 169 | </el-form-item> |
| 170 | 170 | </el-col> |
| 171 | + <el-col :span="24"> | |
| 172 | + <el-form-item label="是否有效" prop="isEffective"> | |
| 173 | + <el-select v-model="dataForm.isEffective" placeholder="请选择"> | |
| 174 | + <el-option | |
| 175 | + v-for="item in effectiveOptions" | |
| 176 | + :key="item.value" | |
| 177 | + :label="item.label" | |
| 178 | + :value="item.value"> | |
| 179 | + </el-option> | |
| 180 | + </el-select> | |
| 181 | + </el-form-item> | |
| 182 | + </el-col> | |
| 171 | 183 | </el-form> |
| 172 | 184 | </el-row> |
| 173 | 185 | <span slot="footer" class="dialog-footer"> |
| ... | ... | @@ -208,6 +220,7 @@ import { init } from 'echarts/lib/echarts'; |
| 208 | 220 | qt1:undefined, |
| 209 | 221 | qt2:undefined, |
| 210 | 222 | beautyType:undefined, |
| 223 | + isEffective: 1, // 默认有效 | |
| 211 | 224 | }, |
| 212 | 225 | rules: { |
| 213 | 226 | }, |
| ... | ... | @@ -219,6 +232,7 @@ import { init } from 'echarts/lib/echarts'; |
| 219 | 232 | options6:[], |
| 220 | 233 | options7:[], |
| 221 | 234 | options8:[], |
| 235 | + effectiveOptions: [], | |
| 222 | 236 | } |
| 223 | 237 | }, |
| 224 | 238 | computed: {}, |
| ... | ... | @@ -335,6 +349,20 @@ import { init } from 'echarts/lib/echarts'; |
| 335 | 349 | this.options8 = [] |
| 336 | 350 | } |
| 337 | 351 | }) |
| 352 | + // 获取状态枚举数据 | |
| 353 | + request({ | |
| 354 | + url: '/api/Extend/lqkdkdjlb/status-enum', | |
| 355 | + method: 'get', | |
| 356 | + }).then(res => { | |
| 357 | + if (res.code == 200 && res.data) { | |
| 358 | + this.effectiveOptions = res.data.map(item => ({ | |
| 359 | + label: item.Name, | |
| 360 | + value: item.Value | |
| 361 | + })); | |
| 362 | + } else { | |
| 363 | + this.effectiveOptions = [] | |
| 364 | + } | |
| 365 | + }) | |
| 338 | 366 | }, |
| 339 | 367 | goBack() { |
| 340 | 368 | this.$emit('refresh') |
| ... | ... | @@ -351,6 +379,7 @@ import { init } from 'echarts/lib/echarts'; |
| 351 | 379 | method: 'get' |
| 352 | 380 | }).then(res =>{ |
| 353 | 381 | this.dataForm = res.data; |
| 382 | + this.dataForm.isEffective = res.data.isEffective?res.data.isEffective:1; | |
| 354 | 383 | }) |
| 355 | 384 | } |
| 356 | 385 | }) | ... | ... |
antis-ncc-admin/src/views/lqXmzl/index.vue
| ... | ... | @@ -140,6 +140,18 @@ |
| 140 | 140 | </el-select> |
| 141 | 141 | </el-form-item> |
| 142 | 142 | </el-col> |
| 143 | + <el-col :span="6"> | |
| 144 | + <el-form-item label="是否有效"> | |
| 145 | + <el-select v-model="query.isEffective" placeholder="请选择状态" clearable> | |
| 146 | + <el-option | |
| 147 | + v-for="item in effectiveSearchOptions" | |
| 148 | + :key="item.value" | |
| 149 | + :label="item.label" | |
| 150 | + :value="item.value"> | |
| 151 | + </el-option> | |
| 152 | + </el-select> | |
| 153 | + </el-form-item> | |
| 154 | + </el-col> | |
| 143 | 155 | </template> |
| 144 | 156 | <el-col :span="6"> |
| 145 | 157 | <el-form-item> |
| ... | ... | @@ -182,10 +194,18 @@ |
| 182 | 194 | <el-table-column prop="fl" label="分类" align="left" /> |
| 183 | 195 | <el-table-column prop="qt1" label="其它1" align="left" /> |
| 184 | 196 | <el-table-column prop="qt2" label="其它2" align="left" /> |
| 197 | + <el-table-column prop="isEffective" label="是否有效" align="left"> | |
| 198 | + <template slot-scope="scope"> | |
| 199 | + <span v-if="scope.row.isEffective === 1" style="color: #67C23A;">有效</span> | |
| 200 | + <span v-else-if="scope.row.isEffective === -1" style="color: #F56C6C;">无效</span> | |
| 201 | + <span v-else-if="scope.row.isEffective === 99" style="color: #909399;">删除</span> | |
| 202 | + <span v-else>无</span> | |
| 203 | + </template> | |
| 204 | + </el-table-column> | |
| 185 | 205 | <el-table-column label="操作" fixed="right" width="100"> |
| 186 | 206 | <template slot-scope="scope"> |
| 187 | 207 | <el-button type="text" @click="addOrUpdateHandle(scope.row.id)" >编辑</el-button> |
| 188 | - <el-button type="text" @click="handleDel(scope.row.id)" class="NCC-table-delBtn" >删除</el-button> | |
| 208 | + <!-- <el-button type="text" @click="handleDel(scope.row.id)" class="NCC-table-delBtn" >删除</el-button> --> | |
| 189 | 209 | </template> |
| 190 | 210 | </el-table-column> |
| 191 | 211 | </NCC-table> |
| ... | ... | @@ -224,6 +244,7 @@ |
| 224 | 244 | qt1:undefined, |
| 225 | 245 | qt2:undefined, |
| 226 | 246 | beautyType:undefined, |
| 247 | + isEffective:undefined, | |
| 227 | 248 | }, |
| 228 | 249 | list: [], |
| 229 | 250 | listLoading: true, |
| ... | ... | @@ -252,6 +273,7 @@ |
| 252 | 273 | { prop: 'fl', label: '分类' }, |
| 253 | 274 | { prop: 'qt1', label: '其它1' }, |
| 254 | 275 | { prop: 'qt2', label: '其它2' }, |
| 276 | + { prop: 'isEffective', label: '是否有效' }, | |
| 255 | 277 | ], |
| 256 | 278 | // 搜索选项数据 |
| 257 | 279 | searchOptions1: [], |
| ... | ... | @@ -262,6 +284,7 @@ |
| 262 | 284 | searchOptions6: [], |
| 263 | 285 | searchOptions7: [], |
| 264 | 286 | searchOptions8: [], |
| 287 | + effectiveSearchOptions: [], | |
| 265 | 288 | } |
| 266 | 289 | }, |
| 267 | 290 | computed: {}, |
| ... | ... | @@ -390,6 +413,21 @@ |
| 390 | 413 | this.searchOptions8 = [] |
| 391 | 414 | } |
| 392 | 415 | }) |
| 416 | + | |
| 417 | + // 获取状态枚举选项 | |
| 418 | + request({ | |
| 419 | + url: '/api/Extend/lqkdkdjlb/status-enum', | |
| 420 | + method: 'get', | |
| 421 | + }).then(res => { | |
| 422 | + if (res.code == 200 && res.data) { | |
| 423 | + this.effectiveSearchOptions = res.data.map(item => ({ | |
| 424 | + label: item.Name, | |
| 425 | + value: item.Value | |
| 426 | + })); | |
| 427 | + } else { | |
| 428 | + this.effectiveSearchOptions = [] | |
| 429 | + } | |
| 430 | + }) | |
| 393 | 431 | }, |
| 394 | 432 | initData() { |
| 395 | 433 | this.listLoading = true; | ... | ... |
antis-ncc-admin/src/views/statisticsList/form1.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="NCC-common-layout"> | |
| 3 | + <div class="NCC-common-layout-center"> | |
| 4 | + <!-- 筛选条件 --> | |
| 5 | + <el-row class="NCC-common-search-box" :gutter="16"> | |
| 6 | + <el-form @submit.native.prevent> | |
| 7 | + <el-col :span="6"> | |
| 8 | + <el-form-item label="门店" required> | |
| 9 | + <el-select v-model="query.storeId" placeholder="请选择门店" clearable filterable> | |
| 10 | + <el-option v-for="store in storeOptions" :key="store.id" :label="store.dm" :value="store.id" /> | |
| 11 | + </el-select> | |
| 12 | + </el-form-item> | |
| 13 | + </el-col> | |
| 14 | + <el-col :span="6"> | |
| 15 | + <el-form-item label="开始时间"> | |
| 16 | + <el-date-picker | |
| 17 | + v-model="query.startTime" | |
| 18 | + type="datetime" | |
| 19 | + value-format="timestamp" | |
| 20 | + format="yyyy-MM-dd HH:mm:ss" | |
| 21 | + placeholder="开始时间" | |
| 22 | + > | |
| 23 | + </el-date-picker> | |
| 24 | + <!-- :picker-options="startTimePickerOptions" --> | |
| 25 | + </el-form-item> | |
| 26 | + </el-col> | |
| 27 | + <el-col :span="6"> | |
| 28 | + <el-form-item label="结束时间"> | |
| 29 | + <el-date-picker | |
| 30 | + v-model="query.endTime" | |
| 31 | + type="datetime" | |
| 32 | + value-format="timestamp" | |
| 33 | + format="yyyy-MM-dd HH:mm:ss" | |
| 34 | + placeholder="结束时间" | |
| 35 | + > | |
| 36 | + <!-- :picker-options="endTimePickerOptions" --> | |
| 37 | + </el-date-picker> | |
| 38 | + </el-form-item> | |
| 39 | + </el-col> | |
| 40 | + <el-col :span="6"> | |
| 41 | + <el-form-item> | |
| 42 | + <el-button type="primary" icon="el-icon-search" @click="search()">查询</el-button> | |
| 43 | + <el-button icon="el-icon-refresh-right" @click="reset()">重置</el-button> | |
| 44 | + </el-form-item> | |
| 45 | + </el-col> | |
| 46 | + </el-form> | |
| 47 | + </el-row> | |
| 48 | + | |
| 49 | + <!-- 统计卡片 --> | |
| 50 | + <div class="statistics-cards" v-if="summaryData"> | |
| 51 | + <el-row :gutter="16"> | |
| 52 | + <el-col :span="6"> | |
| 53 | + <div class="statistics-card"> | |
| 54 | + <div class="card-icon"> | |
| 55 | + <i class="el-icon-document"></i> | |
| 56 | + </div> | |
| 57 | + <div class="card-content"> | |
| 58 | + <div class="card-title">总开单数</div> | |
| 59 | + <div class="card-value">{{ summaryData.totalCount || 0 }}</div> | |
| 60 | + </div> | |
| 61 | + </div> | |
| 62 | + </el-col> | |
| 63 | + <el-col :span="6"> | |
| 64 | + <div class="statistics-card"> | |
| 65 | + <div class="card-icon"> | |
| 66 | + <i class="el-icon-money"></i> | |
| 67 | + </div> | |
| 68 | + <div class="card-content"> | |
| 69 | + <div class="card-title">总金额</div> | |
| 70 | + <div class="card-value">¥{{ formatMoney(summaryData.totalAmount) }}</div> | |
| 71 | + </div> | |
| 72 | + </div> | |
| 73 | + </el-col> | |
| 74 | + <el-col :span="6"> | |
| 75 | + <div class="statistics-card"> | |
| 76 | + <div class="card-icon"> | |
| 77 | + <i class="el-icon-success"></i> | |
| 78 | + </div> | |
| 79 | + <div class="card-content"> | |
| 80 | + <div class="card-title">已付金额</div> | |
| 81 | + <div class="card-value">¥{{ formatMoney(summaryData.totalPaidAmount) }}</div> | |
| 82 | + </div> | |
| 83 | + </div> | |
| 84 | + </el-col> | |
| 85 | + <el-col :span="6"> | |
| 86 | + <div class="statistics-card"> | |
| 87 | + <div class="card-icon"> | |
| 88 | + <i class="el-icon-warning"></i> | |
| 89 | + </div> | |
| 90 | + <div class="card-content"> | |
| 91 | + <div class="card-title">欠款金额</div> | |
| 92 | + <div class="card-value">¥{{ formatMoney(summaryData.totalDebt) }}</div> | |
| 93 | + </div> | |
| 94 | + </div> | |
| 95 | + </el-col> | |
| 96 | + </el-row> | |
| 97 | + <el-row :gutter="16" style="margin-top: 16px;"> | |
| 98 | + <el-col :span="6"> | |
| 99 | + <div class="statistics-card"> | |
| 100 | + <div class="card-icon"> | |
| 101 | + <i class="el-icon-goods"></i> | |
| 102 | + </div> | |
| 103 | + <div class="card-content"> | |
| 104 | + <div class="card-title">购买项目</div> | |
| 105 | + <div class="card-value">{{ summaryData.totalPurchasedItems || 0 }}</div> | |
| 106 | + </div> | |
| 107 | + </div> | |
| 108 | + </el-col> | |
| 109 | + <el-col :span="6"> | |
| 110 | + <div class="statistics-card"> | |
| 111 | + <div class="card-icon"> | |
| 112 | + <i class="el-icon-present"></i> | |
| 113 | + </div> | |
| 114 | + <div class="card-content"> | |
| 115 | + <div class="card-title">赠送项目</div> | |
| 116 | + <div class="card-value">{{ summaryData.totalGiftedItems || 0 }}</div> | |
| 117 | + </div> | |
| 118 | + </div> | |
| 119 | + </el-col> | |
| 120 | + <el-col :span="6"> | |
| 121 | + <div class="statistics-card"> | |
| 122 | + <div class="card-icon"> | |
| 123 | + <i class="el-icon-star-on"></i> | |
| 124 | + </div> | |
| 125 | + <div class="card-content"> | |
| 126 | + <div class="card-title">体验项目</div> | |
| 127 | + <div class="card-value">{{ summaryData.totalExperienceItems || 0 }}</div> | |
| 128 | + </div> | |
| 129 | + </div> | |
| 130 | + </el-col> | |
| 131 | + <el-col :span="6"> | |
| 132 | + <div class="statistics-card"> | |
| 133 | + <div class="card-icon"> | |
| 134 | + <i class="el-icon-user"></i> | |
| 135 | + </div> | |
| 136 | + <div class="card-content"> | |
| 137 | + <div class="card-title">健康师人数</div> | |
| 138 | + <div class="card-value">{{ summaryData.totalHealthTeachers || 0 }}</div> | |
| 139 | + </div> | |
| 140 | + </div> | |
| 141 | + </el-col> | |
| 142 | + </el-row> | |
| 143 | + </div> | |
| 144 | + | |
| 145 | + <!-- 数据表格 --> | |
| 146 | + <div class="NCC-common-layout-main NCC-flex-main"> | |
| 147 | + <!-- <div class="NCC-common-head"> | |
| 148 | + <div> | |
| 149 | + <el-button type="text" icon="el-icon-download" @click="exportData()">导出</el-button> | |
| 150 | + </div> | |
| 151 | + <div class="NCC-common-head-right"> | |
| 152 | + <el-tooltip effect="dark" content="刷新" placement="top"> | |
| 153 | + <el-link icon="icon-ym icon-ym-Refresh NCC-common-head-icon" :underline="false" @click="reset()" /> | |
| 154 | + </el-tooltip> | |
| 155 | + <screenfull isContainer /> | |
| 156 | + </div> | |
| 157 | + </div> --> | |
| 158 | + | |
| 159 | + <NCC-table v-loading="listLoading" :data="records" has-c> | |
| 160 | + <el-table-column prop="date" label="开单日期" align="left" width="120"> | |
| 161 | + <template slot-scope="scope"> | |
| 162 | + {{ formatDate(scope.row.date) }} | |
| 163 | + </template> | |
| 164 | + </el-table-column> | |
| 165 | + <el-table-column prop="customerName" label="客户姓名" align="left" width="100" /> | |
| 166 | + <el-table-column prop="customerPhone" label="客户电话" align="left" width="120" /> | |
| 167 | + <el-table-column prop="goldTriangle" label="金三角" align="left" width="100" /> | |
| 168 | + <el-table-column prop="customerType" label="客户类型" align="left" width="100"> | |
| 169 | + <template slot-scope="scope"> | |
| 170 | + <el-tag :type="scope.row.customerType === '会员' ? 'success' : 'info'" size="small"> | |
| 171 | + {{ scope.row.customerType }} | |
| 172 | + </el-tag> | |
| 173 | + </template> | |
| 174 | + </el-table-column> | |
| 175 | + <el-table-column label="购买项目" align="left" min-width="200"> | |
| 176 | + <template slot-scope="scope"> | |
| 177 | + <div v-if="scope.row.purchasedItems && scope.row.purchasedItems.length > 0"> | |
| 178 | + <div v-for="item in scope.row.purchasedItems" :key="item.id" class="item-row"> | |
| 179 | + <span class="item-name">{{ item.itemName }}</span> | |
| 180 | + <span class="item-info">({{ item.projectNumber }}次 × ¥{{ item.price }})</span> | |
| 181 | + </div> | |
| 182 | + </div> | |
| 183 | + <span v-else class="no-data">无</span> | |
| 184 | + </template> | |
| 185 | + </el-table-column> | |
| 186 | + <el-table-column label="赠送项目" align="left" min-width="200"> | |
| 187 | + <template slot-scope="scope"> | |
| 188 | + <div v-if="scope.row.giftedItems && scope.row.giftedItems.length > 0"> | |
| 189 | + <div v-for="item in scope.row.giftedItems" :key="item.id" class="item-row"> | |
| 190 | + <span class="item-name">{{ item.itemName }}</span> | |
| 191 | + <span class="item-info">({{ item.projectNumber }}次)</span> | |
| 192 | + <span v-if="item.remark" class="item-remark">{{ item.remark }}</span> | |
| 193 | + </div> | |
| 194 | + </div> | |
| 195 | + <span v-else class="no-data">无</span> | |
| 196 | + </template> | |
| 197 | + </el-table-column> | |
| 198 | + <el-table-column label="体验项目" align="left" min-width="200"> | |
| 199 | + <template slot-scope="scope"> | |
| 200 | + <div v-if="scope.row.experienceItems && scope.row.experienceItems.length > 0"> | |
| 201 | + <div v-for="item in scope.row.experienceItems" :key="item.id" class="item-row"> | |
| 202 | + <span class="item-name">{{ item.itemName }}</span> | |
| 203 | + <span class="item-info">({{ item.projectNumber }}次)</span> | |
| 204 | + </div> | |
| 205 | + </div> | |
| 206 | + <span v-else class="no-data">无</span> | |
| 207 | + </template> | |
| 208 | + </el-table-column> | |
| 209 | + <el-table-column label="健康师" align="left" min-width="150"> | |
| 210 | + <template slot-scope="scope"> | |
| 211 | + <div v-if="scope.row.healthTeachers && scope.row.healthTeachers.length > 0"> | |
| 212 | + <div v-for="teacher in scope.row.healthTeachers" :key="teacher.teacherId" class="teacher-row"> | |
| 213 | + <span class="teacher-name">{{ teacher.teacherName }}</span> | |
| 214 | + <span class="teacher-performance">业绩: ¥{{ teacher.performance }}</span> | |
| 215 | + </div> | |
| 216 | + </div> | |
| 217 | + <span v-else class="no-data">无</span> | |
| 218 | + </template> | |
| 219 | + </el-table-column> | |
| 220 | + <el-table-column prop="paidAmount" label="已付金额" align="left" width="100"> | |
| 221 | + <template slot-scope="scope"> | |
| 222 | + <span class="amount-paid">¥{{ formatMoney(scope.row.paidAmount) }}</span> | |
| 223 | + </template> | |
| 224 | + </el-table-column> | |
| 225 | + <el-table-column prop="debtAmount" label="欠款金额" align="left" width="100"> | |
| 226 | + <template slot-scope="scope"> | |
| 227 | + <span class="amount-debt">¥{{ formatMoney(scope.row.debtAmount) }}</span> | |
| 228 | + </template> | |
| 229 | + </el-table-column> | |
| 230 | + <el-table-column prop="totalAmount" label="总金额" align="left" width="100"> | |
| 231 | + <template slot-scope="scope"> | |
| 232 | + <span class="amount-total">¥{{ formatMoney(scope.row.totalAmount) }}</span> | |
| 233 | + </template> | |
| 234 | + </el-table-column> | |
| 235 | + <el-table-column prop="paymentMethod" label="支付方式" align="left" width="100" /> | |
| 236 | + <el-table-column prop="remark" label="备注" align="left" min-width="150"> | |
| 237 | + <template slot-scope="scope"> | |
| 238 | + {{ scope.row.remark || '无' }} | |
| 239 | + </template> | |
| 240 | + </el-table-column> | |
| 241 | + <el-table-column label="开单时间" prop="createTime" align="left" width="150"> | |
| 242 | + <template slot-scope="scope"> | |
| 243 | + {{ formatTime(scope.row.createTime) }} | |
| 244 | + </template> | |
| 245 | + </el-table-column> | |
| 246 | + </NCC-table> | |
| 247 | + </div> | |
| 248 | + </div> | |
| 249 | + </div> | |
| 250 | +</template> | |
| 251 | + | |
| 252 | +<script> | |
| 253 | +import request from '@/utils/request' | |
| 254 | + | |
| 255 | +export default { | |
| 256 | + name: 'BillingRecordSummary', | |
| 257 | + data() { | |
| 258 | + return { | |
| 259 | + query: { | |
| 260 | + storeId: undefined, | |
| 261 | + startTime: undefined, | |
| 262 | + endTime: undefined | |
| 263 | + }, | |
| 264 | + storeOptions: [], | |
| 265 | + summaryData: null, | |
| 266 | + records: [], | |
| 267 | + listLoading: false, | |
| 268 | + startTimePickerOptions: { | |
| 269 | + disabledDate: (time) => { | |
| 270 | + const timeMs = time.getTime() | |
| 271 | + const nowMs = Date.now() | |
| 272 | + const ninetyDaysMs = 90 * 24 * 60 * 60 * 1000 | |
| 273 | + // 不能选择未来日期 | |
| 274 | + if (timeMs > nowMs) return true | |
| 275 | + // 若已选择结束时间:开始时间需在 [end-90天, end] 区间内 | |
| 276 | + if (this.query.endTime) { | |
| 277 | + const minStart = this.query.endTime - ninetyDaysMs | |
| 278 | + return timeMs < minStart || timeMs > this.query.endTime | |
| 279 | + } | |
| 280 | + return false | |
| 281 | + } | |
| 282 | + }, | |
| 283 | + endTimePickerOptions: { | |
| 284 | + disabledDate: (time) => { | |
| 285 | + const timeMs = time.getTime() | |
| 286 | + const nowMs = Date.now() | |
| 287 | + const ninetyDaysMs = 90 * 24 * 60 * 60 * 1000 | |
| 288 | + // 不能选择未来日期 | |
| 289 | + if (timeMs > nowMs) return true | |
| 290 | + // 若已选择开始时间:结束时间需在 [start, start+90天] 区间内 | |
| 291 | + if (this.query.startTime) { | |
| 292 | + const maxEnd = this.query.startTime + ninetyDaysMs | |
| 293 | + return timeMs < this.query.startTime || timeMs > maxEnd | |
| 294 | + } | |
| 295 | + return false | |
| 296 | + } | |
| 297 | + } | |
| 298 | + } | |
| 299 | + }, | |
| 300 | + created() { | |
| 301 | + this.initStoreOptions() | |
| 302 | + this.setDefaultTimeRange() | |
| 303 | + }, | |
| 304 | + methods: { | |
| 305 | + // 初始化门店选项 | |
| 306 | + initStoreOptions() { | |
| 307 | + request({ | |
| 308 | + url: '/api/Extend/LqMdxx', | |
| 309 | + method: 'GET', | |
| 310 | + data: { | |
| 311 | + currentPage: 1, | |
| 312 | + pageSize: 1000 | |
| 313 | + } | |
| 314 | + }).then(res => { | |
| 315 | + this.storeOptions = res.data.list || [] | |
| 316 | + }).catch(err => { | |
| 317 | + console.error('获取门店列表失败:', err) | |
| 318 | + }) | |
| 319 | + }, | |
| 320 | + | |
| 321 | + // 设置默认时间范围(最近三个月) | |
| 322 | + setDefaultTimeRange() { | |
| 323 | + const now = new Date() | |
| 324 | + const threeMonthsAgo = new Date() | |
| 325 | + threeMonthsAgo.setMonth(now.getMonth() - 3) | |
| 326 | + | |
| 327 | + this.query.startTime = threeMonthsAgo.getTime() | |
| 328 | + this.query.endTime = now.getTime() | |
| 329 | + }, | |
| 330 | + | |
| 331 | + // 查询数据 | |
| 332 | + search() { | |
| 333 | + if (!this.query.storeId) { | |
| 334 | + this.$message({ | |
| 335 | + type: 'warning', | |
| 336 | + message: '请选择门店', | |
| 337 | + duration: 1500 | |
| 338 | + }) | |
| 339 | + return | |
| 340 | + } | |
| 341 | + | |
| 342 | + this.listLoading = true | |
| 343 | + | |
| 344 | + const params = { | |
| 345 | + storeId: this.query.storeId, | |
| 346 | + startTime: this.query.startTime ? this.formatDateTime(this.query.startTime) : undefined, | |
| 347 | + endTime: this.query.endTime ? this.formatDateTime(this.query.endTime) : undefined | |
| 348 | + } | |
| 349 | + | |
| 350 | + request({ | |
| 351 | + url: '/api/Extend/lqkdkdjlb/GetBillingRecordSummaryByStoreId', | |
| 352 | + method: 'GET', | |
| 353 | + data: params | |
| 354 | + }).then(res => { | |
| 355 | + if (res.data && res.data.data) { | |
| 356 | + this.summaryData = res.data.data.summary | |
| 357 | + this.records = res.data.data.records || [] | |
| 358 | + } else { | |
| 359 | + this.summaryData = null | |
| 360 | + this.records = [] | |
| 361 | + } | |
| 362 | + this.listLoading = false | |
| 363 | + }).catch(err => { | |
| 364 | + console.error('查询失败:', err) | |
| 365 | + this.$message({ | |
| 366 | + type: 'error', | |
| 367 | + message: '查询失败,请重试', | |
| 368 | + duration: 1500 | |
| 369 | + }) | |
| 370 | + this.listLoading = false | |
| 371 | + }) | |
| 372 | + }, | |
| 373 | + | |
| 374 | + // 重置查询条件 | |
| 375 | + reset() { | |
| 376 | + this.query = { | |
| 377 | + storeId: undefined, | |
| 378 | + startTime: undefined, | |
| 379 | + endTime: undefined | |
| 380 | + } | |
| 381 | + this.summaryData = null | |
| 382 | + this.records = [] | |
| 383 | + this.setDefaultTimeRange() | |
| 384 | + }, | |
| 385 | + | |
| 386 | + // 导出数据 | |
| 387 | + exportData() { | |
| 388 | + if (!this.query.storeId) { | |
| 389 | + this.$message({ | |
| 390 | + type: 'warning', | |
| 391 | + message: '请先选择门店并查询数据', | |
| 392 | + duration: 1500 | |
| 393 | + }) | |
| 394 | + return | |
| 395 | + } | |
| 396 | + | |
| 397 | + const params = { | |
| 398 | + storeId: this.query.storeId, | |
| 399 | + startTime: this.query.startTime, | |
| 400 | + endTime: this.query.endTime | |
| 401 | + } | |
| 402 | + | |
| 403 | + // 这里可以调用导出接口 | |
| 404 | + this.$message({ | |
| 405 | + type: 'info', | |
| 406 | + message: '导出功能开发中...', | |
| 407 | + duration: 1500 | |
| 408 | + }) | |
| 409 | + }, | |
| 410 | + | |
| 411 | + // 格式化金额 | |
| 412 | + formatMoney(amount) { | |
| 413 | + if (!amount && amount !== 0) return '0.00' | |
| 414 | + return Number(amount).toFixed(2) | |
| 415 | + }, | |
| 416 | + | |
| 417 | + // 格式化日期 | |
| 418 | + formatDate(dateStr) { | |
| 419 | + if (!dateStr) return '无' | |
| 420 | + return dateStr | |
| 421 | + }, | |
| 422 | + | |
| 423 | + // 格式化时间 | |
| 424 | + formatTime(timestamp) { | |
| 425 | + if (!timestamp) return '无' | |
| 426 | + const date = new Date(timestamp) | |
| 427 | + return date.toLocaleString('zh-CN', { | |
| 428 | + year: 'numeric', | |
| 429 | + month: '2-digit', | |
| 430 | + day: '2-digit', | |
| 431 | + hour: '2-digit', | |
| 432 | + minute: '2-digit', | |
| 433 | + second: '2-digit' | |
| 434 | + }) | |
| 435 | + }, | |
| 436 | + | |
| 437 | + // 格式化日期时间(用于API传参) | |
| 438 | + formatDateTime(timestamp) { | |
| 439 | + if (!timestamp) return '' | |
| 440 | + const date = new Date(timestamp) | |
| 441 | + const year = date.getFullYear() | |
| 442 | + const month = String(date.getMonth() + 1).padStart(2, '0') | |
| 443 | + const day = String(date.getDate()).padStart(2, '0') | |
| 444 | + const hours = String(date.getHours()).padStart(2, '0') | |
| 445 | + const minutes = String(date.getMinutes()).padStart(2, '0') | |
| 446 | + const seconds = String(date.getSeconds()).padStart(2, '0') | |
| 447 | + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}` | |
| 448 | + } | |
| 449 | + } | |
| 450 | +} | |
| 451 | +</script> | |
| 452 | + | |
| 453 | +<style lang="scss" scoped> | |
| 454 | +.statistics-cards { | |
| 455 | + margin-bottom: 20px; | |
| 456 | +} | |
| 457 | + | |
| 458 | +.statistics-card { | |
| 459 | + height: 100px; | |
| 460 | + padding: 12px; | |
| 461 | + border-radius: 12px; | |
| 462 | + background: #fff; | |
| 463 | + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); | |
| 464 | + display: flex; | |
| 465 | + align-items: center; | |
| 466 | + | |
| 467 | + .card-icon { | |
| 468 | + width: 60px; | |
| 469 | + height: 60px; | |
| 470 | + border-radius: 8px; | |
| 471 | + background: linear-gradient(135deg, #409EFF, #67C23A); | |
| 472 | + display: flex; | |
| 473 | + align-items: center; | |
| 474 | + justify-content: center; | |
| 475 | + margin-right: 16px; | |
| 476 | + | |
| 477 | + i { | |
| 478 | + font-size: 24px; | |
| 479 | + color: #fff; | |
| 480 | + } | |
| 481 | + } | |
| 482 | + | |
| 483 | + .card-content { | |
| 484 | + flex: 1; | |
| 485 | + | |
| 486 | + .card-title { | |
| 487 | + font-size: 14px; | |
| 488 | + color: #909399; | |
| 489 | + margin-bottom: 8px; | |
| 490 | + } | |
| 491 | + | |
| 492 | + .card-value { | |
| 493 | + font-size: 24px; | |
| 494 | + font-weight: bold; | |
| 495 | + color: #303133; | |
| 496 | + } | |
| 497 | + } | |
| 498 | +} | |
| 499 | + | |
| 500 | +.item-row, .teacher-row { | |
| 501 | + margin-bottom: 4px; | |
| 502 | + | |
| 503 | + &:last-child { | |
| 504 | + margin-bottom: 0; | |
| 505 | + } | |
| 506 | +} | |
| 507 | + | |
| 508 | +.item-name, .teacher-name { | |
| 509 | + font-weight: 500; | |
| 510 | + color: #303133; | |
| 511 | +} | |
| 512 | + | |
| 513 | +.item-info, .teacher-performance { | |
| 514 | + font-size: 12px; | |
| 515 | + color: #909399; | |
| 516 | + margin-left: 8px; | |
| 517 | +} | |
| 518 | + | |
| 519 | +.item-remark { | |
| 520 | + font-size: 12px; | |
| 521 | + color: #F56C6C; | |
| 522 | + margin-left: 8px; | |
| 523 | +} | |
| 524 | + | |
| 525 | +.no-data { | |
| 526 | + color: #C0C4CC; | |
| 527 | + font-style: italic; | |
| 528 | +} | |
| 529 | + | |
| 530 | +.amount-paid { | |
| 531 | + color: #67C23A; | |
| 532 | + font-weight: 500; | |
| 533 | +} | |
| 534 | + | |
| 535 | +.amount-debt { | |
| 536 | + color: #F56C6C; | |
| 537 | + font-weight: 500; | |
| 538 | +} | |
| 539 | + | |
| 540 | +.amount-total { | |
| 541 | + color: #409EFF; | |
| 542 | + font-weight: 500; | |
| 543 | +} | |
| 544 | + | |
| 545 | +// 响应式设计 | |
| 546 | +@media (max-width: 768px) { | |
| 547 | + .statistics-cards { | |
| 548 | + .el-col { | |
| 549 | + margin-bottom: 16px; | |
| 550 | + } | |
| 551 | + } | |
| 552 | + | |
| 553 | + .statistics-card { | |
| 554 | + height: 80px; | |
| 555 | + padding: 8px; | |
| 556 | + | |
| 557 | + .card-icon { | |
| 558 | + width: 50px; | |
| 559 | + height: 50px; | |
| 560 | + margin-right: 12px; | |
| 561 | + | |
| 562 | + i { | |
| 563 | + font-size: 20px; | |
| 564 | + } | |
| 565 | + } | |
| 566 | + | |
| 567 | + .card-content { | |
| 568 | + .card-title { | |
| 569 | + font-size: 12px; | |
| 570 | + } | |
| 571 | + | |
| 572 | + .card-value { | |
| 573 | + font-size: 18px; | |
| 574 | + } | |
| 575 | + } | |
| 576 | + } | |
| 577 | +} | |
| 578 | +</style> | ... | ... |