Commit f86c9c6930aa93078c9289dd5d97ca682cd50adc
1 parent
4b31ecb4
添加库存管理和学习班级管理功能
前端功能: - 新增库存管理页面,包含库存列表、添加/编辑库存、库存详情等功能 - 新增库存使用记录管理,支持添加使用记录、查看使用记录列表等 - 新增学习班级管理页面,包含班级列表、创建班级、添加学员等功能 - 新增学习记录管理,支持添加学习记录、查看学员列表等 - 完善API接口调用和路由配置 后端功能: - 完善LqInventoryService库存管理服务 - 完善LqInventoryUsageService库存使用记录服务 - 优化服务接口和业务逻辑 技术特点: - 使用Vue 2 + Element UI构建现代化管理界面 - 支持分页查询、条件筛选、批量操作等功能 - 响应式设计,适配不同屏幕尺寸 - 统一的错误处理和用户提示
Showing
16 changed files
with
3009 additions
and
15 deletions
antis-ncc-admin/src/api/extend/lqInventory.js
0 → 100644
| 1 | +import request from '@/utils/request' | |
| 2 | + | |
| 3 | +// ========== 库存管理接口 ========== | |
| 4 | + | |
| 5 | +// 创建库存 | |
| 6 | +export function createInventory(data) { | |
| 7 | + return request({ | |
| 8 | + url: '/api/Extend/LqInventory/Create', | |
| 9 | + method: 'post', | |
| 10 | + data | |
| 11 | + }) | |
| 12 | +} | |
| 13 | + | |
| 14 | +// 更新库存 | |
| 15 | +export function updateInventory(data) { | |
| 16 | + return request({ | |
| 17 | + url: '/api/Extend/LqInventory/Update', | |
| 18 | + method: 'put', | |
| 19 | + data | |
| 20 | + }) | |
| 21 | +} | |
| 22 | + | |
| 23 | +// 获取库存列表 | |
| 24 | +export function getInventoryList(params) { | |
| 25 | + return request({ | |
| 26 | + url: '/api/Extend/LqInventory/GetList', | |
| 27 | + method: 'get', | |
| 28 | + data: params | |
| 29 | + }) | |
| 30 | +} | |
| 31 | + | |
| 32 | +// 获取库存详情 | |
| 33 | +export function getInventoryInfo(id) { | |
| 34 | + return request({ | |
| 35 | + url: '/api/Extend/LqInventory/GetInfo', | |
| 36 | + method: 'get', | |
| 37 | + data: { id } | |
| 38 | + }) | |
| 39 | +} | |
| 40 | + | |
| 41 | +// ========== 库存使用记录接口 ========== | |
| 42 | + | |
| 43 | +// 添加库存使用记录 | |
| 44 | +export function createInventoryUsage(data) { | |
| 45 | + return request({ | |
| 46 | + url: '/api/Extend/LqInventoryUsage/Create', | |
| 47 | + method: 'post', | |
| 48 | + data | |
| 49 | + }) | |
| 50 | +} | |
| 51 | + | |
| 52 | +// 作废库存使用记录 | |
| 53 | +export function cancelInventoryUsage(id, remarks) { | |
| 54 | + return request({ | |
| 55 | + url: '/api/Extend/LqInventoryUsage/Cancel', | |
| 56 | + method: 'put', | |
| 57 | + data: { id, remarks } | |
| 58 | + }) | |
| 59 | +} | |
| 60 | + | |
| 61 | +// 获取使用记录列表 | |
| 62 | +export function getInventoryUsageList(params) { | |
| 63 | + return request({ | |
| 64 | + url: '/api/Extend/LqInventoryUsage/GetList', | |
| 65 | + method: 'get', | |
| 66 | + data: params | |
| 67 | + }) | |
| 68 | +} | |
| 69 | + | |
| 70 | +// 获取产品使用统计 | |
| 71 | +export function getProductUsageStatistics(data) { | |
| 72 | + return request({ | |
| 73 | + url: '/api/Extend/LqInventoryUsage/GetProductUsageStatistics', | |
| 74 | + method: 'post', | |
| 75 | + data | |
| 76 | + }) | |
| 77 | +} | |
| 78 | + | |
| 79 | +// 获取门店使用统计 | |
| 80 | +export function getStoreUsageStatistics(data) { | |
| 81 | + return request({ | |
| 82 | + url: '/api/Extend/LqInventoryUsage/GetStoreUsageStatistics', | |
| 83 | + method: 'post', | |
| 84 | + data | |
| 85 | + }) | |
| 86 | +} | |
| 87 | + | |
| 88 | +// 获取使用趋势统计 | |
| 89 | +export function getUsageTrendStatistics(data) { | |
| 90 | + return request({ | |
| 91 | + url: '/api/Extend/LqInventoryUsage/GetUsageTrendStatistics', | |
| 92 | + method: 'post', | |
| 93 | + data | |
| 94 | + }) | |
| 95 | +} | |
| 96 | + | |
| 97 | +// 获取产品使用排行榜 | |
| 98 | +export function getProductUsageRanking(data) { | |
| 99 | + return request({ | |
| 100 | + url: '/api/Extend/LqInventoryUsage/GetProductUsageRanking', | |
| 101 | + method: 'post', | |
| 102 | + data | |
| 103 | + }) | |
| 104 | +} | ... | ... |
antis-ncc-admin/src/api/extend/lqStudyClass.js
0 → 100644
| 1 | +import request from '@/utils/request' | |
| 2 | + | |
| 3 | +// 创建学习班级并添加学员 | |
| 4 | +export function createClassWithStudents(data) { | |
| 5 | + return request({ | |
| 6 | + url: '/api/Extend/LqStudyClass/CreateClassWithStudents', | |
| 7 | + method: 'post', | |
| 8 | + data | |
| 9 | + }) | |
| 10 | +} | |
| 11 | + | |
| 12 | +// 向现有班级添加学员 | |
| 13 | +export function addStudentsToClass(data) { | |
| 14 | + return request({ | |
| 15 | + url: '/api/Extend/LqStudyClass/AddStudentsToClass', | |
| 16 | + method: 'post', | |
| 17 | + data | |
| 18 | + }) | |
| 19 | +} | |
| 20 | + | |
| 21 | +// 获取所有班级列表 | |
| 22 | +export function getClassList(params) { | |
| 23 | + return request({ | |
| 24 | + url: '/api/Extend/LqStudyClass/GetClassList', | |
| 25 | + method: 'get', | |
| 26 | + data: params | |
| 27 | + }) | |
| 28 | +} | |
| 29 | + | |
| 30 | +// 获取班级下所有学员信息 | |
| 31 | +export function getStudentListByClassId(params) { | |
| 32 | + return request({ | |
| 33 | + url: '/api/Extend/LqStudyClass/GetStudentListByClassId', | |
| 34 | + method: 'get', | |
| 35 | + data: params | |
| 36 | + }) | |
| 37 | +} | |
| 38 | + | |
| 39 | +// 添加学习记录 | |
| 40 | +export function addStudyRecord(data) { | |
| 41 | + return request({ | |
| 42 | + url: '/api/Extend/LqStudyClass/AddStudyRecord', | |
| 43 | + method: 'post', | |
| 44 | + data | |
| 45 | + }) | |
| 46 | +} | |
| 47 | + | |
| 48 | +// 获取学习记录列表 | |
| 49 | +export function getStudyRecordList(params) { | |
| 50 | + return request({ | |
| 51 | + url: '/api/Extend/LqStudyClass/GetStudyRecordList', | |
| 52 | + method: 'get', | |
| 53 | + data: params | |
| 54 | + }) | |
| 55 | +} | |
| 56 | + | |
| 57 | +// 作废学习记录 | |
| 58 | +export function cancelStudyRecord(id) { | |
| 59 | + return request({ | |
| 60 | + url: '/api/Extend/LqStudyClass/CancelStudyRecord', | |
| 61 | + method: 'post', | |
| 62 | + data: id | |
| 63 | + }) | |
| 64 | +} | |
| 65 | + | ... | ... |
antis-ncc-admin/src/router/modules/base.js
| ... | ... | @@ -76,6 +76,17 @@ const baseRouter = [{ |
| 76 | 76 | icon: 'icon-ym icon-ym-s-data', |
| 77 | 77 | } |
| 78 | 78 | }, |
| 79 | + { | |
| 80 | + path: '/lqInventory', | |
| 81 | + component: (resolve) => require(['@/views/lqInventory'], resolve), | |
| 82 | + name: 'lqInventory', | |
| 83 | + meta: { | |
| 84 | + title: 'lqInventory', | |
| 85 | + affix: false, | |
| 86 | + zhTitle: '库存管理', | |
| 87 | + icon: 'icon-ym icon-ym-box', | |
| 88 | + } | |
| 89 | + }, | |
| 79 | 90 | { |
| 80 | 91 | path: '/salaryCalculation', |
| 81 | 92 | component: (resolve) => require(['@/views/salaryCalculation/index'], resolve), | ... | ... |
antis-ncc-admin/src/views/lqInventory/AddUsageRecordForm.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog title="添加库存使用记录" :visible.sync="visible" width="600px" @close="closeDialog"> | |
| 3 | + <el-form ref="form" :model="form" :rules="rules" label-width="100px"> | |
| 4 | + <el-row :gutter="20"> | |
| 5 | + <el-col :span="12"> | |
| 6 | + <el-form-item label="产品" prop="productId"> | |
| 7 | + <el-select v-model="form.productId" placeholder="请选择产品" style="width: 100%" | |
| 8 | + @change="onProductChange"> | |
| 9 | + <el-option v-for="product in productList" :key="product.id" :label="product.productName" | |
| 10 | + :value="product.id"> | |
| 11 | + <span style="float: left">{{ product.productName }}</span> | |
| 12 | + <span style="float: right; color: #8492a6; font-size: 13px">{{ product.productCategory | |
| 13 | + }}</span> | |
| 14 | + </el-option> | |
| 15 | + </el-select> | |
| 16 | + </el-form-item> | |
| 17 | + </el-col> | |
| 18 | + <el-col :span="12"> | |
| 19 | + <el-form-item label="使用门店" prop="storeId"> | |
| 20 | + <userSelect v-model="form.storeId" placeholder="请选择使用门店" @change="onStoreChange" /> | |
| 21 | + </el-form-item> | |
| 22 | + </el-col> | |
| 23 | + </el-row> | |
| 24 | + | |
| 25 | + <el-row :gutter="20"> | |
| 26 | + <el-col :span="12"> | |
| 27 | + <el-form-item label="使用数量" prop="usageQuantity"> | |
| 28 | + <el-input-number v-model="form.usageQuantity" :min="1" style="width: 100%" | |
| 29 | + placeholder="请输入使用数量" /> | |
| 30 | + </el-form-item> | |
| 31 | + </el-col> | |
| 32 | + <el-col :span="12"> | |
| 33 | + <el-form-item label="使用时间" prop="usageTime"> | |
| 34 | + <el-date-picker v-model="form.usageTime" type="date" placeholder="选择使用时间" | |
| 35 | + value-format="yyyy-MM-dd" style="width: 100%" /> | |
| 36 | + </el-form-item> | |
| 37 | + </el-col> | |
| 38 | + </el-row> | |
| 39 | + | |
| 40 | + <el-form-item label="关联消费ID"> | |
| 41 | + <el-input v-model="form.relatedConsumeId" placeholder="请输入关联消费ID(可选)" /> | |
| 42 | + </el-form-item> | |
| 43 | + | |
| 44 | + <el-form-item label="备注"> | |
| 45 | + <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注信息" /> | |
| 46 | + </el-form-item> | |
| 47 | + | |
| 48 | + <!-- 产品信息显示 --> | |
| 49 | + <div v-if="selectedProduct" class="product-info"> | |
| 50 | + <el-divider content-position="left">产品信息</el-divider> | |
| 51 | + <el-row :gutter="20"> | |
| 52 | + <el-col :span="8"> | |
| 53 | + <div class="info-item"> | |
| 54 | + <label>产品分类:</label> | |
| 55 | + <span>{{ selectedProduct.productCategory }}</span> | |
| 56 | + </div> | |
| 57 | + </el-col> | |
| 58 | + <el-col :span="8"> | |
| 59 | + <div class="info-item"> | |
| 60 | + <label>单价:</label> | |
| 61 | + <span class="price">{{ formatMoney(selectedProduct.price) }}</span> | |
| 62 | + </div> | |
| 63 | + </el-col> | |
| 64 | + <el-col :span="8"> | |
| 65 | + <div class="info-item"> | |
| 66 | + <label>当前库存:</label> | |
| 67 | + <span :class="selectedProduct.quantity <= 10 ? 'text-danger' : ''">{{ | |
| 68 | + selectedProduct.quantity }}</span> | |
| 69 | + </div> | |
| 70 | + </el-col> | |
| 71 | + </el-row> | |
| 72 | + </div> | |
| 73 | + </el-form> | |
| 74 | + | |
| 75 | + <div slot="footer" class="dialog-footer"> | |
| 76 | + <el-button @click="closeDialog">取消</el-button> | |
| 77 | + <el-button type="primary" @click="submitForm" :loading="submitLoading">确定</el-button> | |
| 78 | + </div> | |
| 79 | + </el-dialog> | |
| 80 | +</template> | |
| 81 | + | |
| 82 | +<script> | |
| 83 | +import { createInventoryUsage, getInventoryList } from '@/api/extend/lqInventory' | |
| 84 | + | |
| 85 | +export default { | |
| 86 | + name: 'AddUsageRecordForm', | |
| 87 | + data() { | |
| 88 | + return { | |
| 89 | + visible: false, | |
| 90 | + submitLoading: false, | |
| 91 | + productId: '', // 特定产品ID | |
| 92 | + productName: '', // 特定产品名称 | |
| 93 | + productList: [], // 产品列表 | |
| 94 | + selectedProduct: null, // 选中的产品 | |
| 95 | + form: { | |
| 96 | + productId: '', | |
| 97 | + storeId: '', | |
| 98 | + usageQuantity: 1, | |
| 99 | + usageTime: '', | |
| 100 | + relatedConsumeId: '', | |
| 101 | + remark: '' | |
| 102 | + }, | |
| 103 | + rules: { | |
| 104 | + productId: [ | |
| 105 | + { required: true, message: '请选择产品', trigger: 'change' } | |
| 106 | + ], | |
| 107 | + storeId: [ | |
| 108 | + { required: true, message: '请选择使用门店', trigger: 'change' } | |
| 109 | + ], | |
| 110 | + usageQuantity: [ | |
| 111 | + { required: true, message: '请输入使用数量', trigger: 'blur' } | |
| 112 | + ], | |
| 113 | + usageTime: [ | |
| 114 | + { required: true, message: '请选择使用时间', trigger: 'change' } | |
| 115 | + ] | |
| 116 | + } | |
| 117 | + } | |
| 118 | + }, | |
| 119 | + methods: { | |
| 120 | + init(productId = '', productName = '') { | |
| 121 | + this.visible = true | |
| 122 | + this.productId = productId | |
| 123 | + this.productName = productName | |
| 124 | + this.getProductList() | |
| 125 | + this.resetForm() | |
| 126 | + }, | |
| 127 | + | |
| 128 | + // 获取产品列表 | |
| 129 | + getProductList() { | |
| 130 | + const params = { | |
| 131 | + currentPage: 1, | |
| 132 | + pageSize: 1000, // 获取所有产品 | |
| 133 | + isEffective: 1 // 只获取有效产品 | |
| 134 | + } | |
| 135 | + | |
| 136 | + getInventoryList(params).then(response => { | |
| 137 | + if (response.code === 200) { | |
| 138 | + this.productList = response.data.list || [] | |
| 139 | + // 如果指定了产品ID,自动选中 | |
| 140 | + if (this.productId) { | |
| 141 | + this.form.productId = this.productId | |
| 142 | + this.onProductChange(this.productId) | |
| 143 | + } | |
| 144 | + } else { | |
| 145 | + this.$message.error(response.msg || '获取产品列表失败') | |
| 146 | + } | |
| 147 | + }) | |
| 148 | + }, | |
| 149 | + | |
| 150 | + // 产品选择变化 | |
| 151 | + onProductChange(productId) { | |
| 152 | + this.selectedProduct = this.productList.find(product => product.id === productId) | |
| 153 | + }, | |
| 154 | + | |
| 155 | + // 门店选择变化 | |
| 156 | + onStoreChange(ids, users) { | |
| 157 | + // userSelect组件的回调处理 | |
| 158 | + }, | |
| 159 | + | |
| 160 | + resetForm() { | |
| 161 | + this.form = { | |
| 162 | + productId: this.productId || '', | |
| 163 | + storeId: '', | |
| 164 | + usageQuantity: 1, | |
| 165 | + usageTime: '', | |
| 166 | + relatedConsumeId: '', | |
| 167 | + remark: '' | |
| 168 | + } | |
| 169 | + this.selectedProduct = null | |
| 170 | + this.$nextTick(() => { | |
| 171 | + if (this.$refs.form) { | |
| 172 | + this.$refs.form.clearValidate() | |
| 173 | + } | |
| 174 | + }) | |
| 175 | + }, | |
| 176 | + | |
| 177 | + // 提交表单 | |
| 178 | + submitForm() { | |
| 179 | + this.$refs.form.validate((valid) => { | |
| 180 | + if (valid) { | |
| 181 | + // 检查库存数量 | |
| 182 | + if (this.selectedProduct && this.form.usageQuantity > this.selectedProduct.quantity) { | |
| 183 | + this.$message.error(`库存不足,当前库存:${this.selectedProduct.quantity},需要数量:${this.form.usageQuantity}`) | |
| 184 | + return | |
| 185 | + } | |
| 186 | + | |
| 187 | + this.submitLoading = true | |
| 188 | + createInventoryUsage(this.form).then(response => { | |
| 189 | + if (response.code === 200) { | |
| 190 | + this.$message.success('添加成功') | |
| 191 | + this.closeDialog() | |
| 192 | + this.$emit('refreshDataList') | |
| 193 | + } else { | |
| 194 | + this.$message.error(response.msg || '添加失败') | |
| 195 | + } | |
| 196 | + this.submitLoading = false | |
| 197 | + }).catch(() => { | |
| 198 | + this.submitLoading = false | |
| 199 | + }) | |
| 200 | + } | |
| 201 | + }) | |
| 202 | + }, | |
| 203 | + | |
| 204 | + // 格式化金额 | |
| 205 | + formatMoney(value) { | |
| 206 | + if (value === null || value === undefined || value === '') { | |
| 207 | + return '0.00' | |
| 208 | + } | |
| 209 | + return Number(value).toFixed(2) | |
| 210 | + }, | |
| 211 | + | |
| 212 | + // 关闭弹窗 | |
| 213 | + closeDialog() { | |
| 214 | + this.visible = false | |
| 215 | + this.resetForm() | |
| 216 | + } | |
| 217 | + } | |
| 218 | +} | |
| 219 | +</script> | |
| 220 | + | |
| 221 | +<style scoped> | |
| 222 | +.dialog-footer { | |
| 223 | + text-align: right; | |
| 224 | +} | |
| 225 | + | |
| 226 | +.product-info { | |
| 227 | + margin-top: 20px; | |
| 228 | + padding: 15px; | |
| 229 | + background: #f5f7fa; | |
| 230 | + border-radius: 4px; | |
| 231 | +} | |
| 232 | + | |
| 233 | +.info-item { | |
| 234 | + margin-bottom: 10px; | |
| 235 | + display: flex; | |
| 236 | + align-items: center; | |
| 237 | +} | |
| 238 | + | |
| 239 | +.info-item label { | |
| 240 | + font-weight: bold; | |
| 241 | + color: #606266; | |
| 242 | + min-width: 80px; | |
| 243 | + margin-right: 10px; | |
| 244 | +} | |
| 245 | + | |
| 246 | +.info-item span { | |
| 247 | + color: #303133; | |
| 248 | +} | |
| 249 | + | |
| 250 | +.price { | |
| 251 | + color: #e6a23c; | |
| 252 | + font-weight: bold; | |
| 253 | +} | |
| 254 | + | |
| 255 | +.text-danger { | |
| 256 | + color: #f56c6c; | |
| 257 | + font-weight: bold; | |
| 258 | +} | |
| 259 | +</style> | ... | ... |
antis-ncc-admin/src/views/lqInventory/InventoryForm.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog :title="isEdit ? '编辑库存' : '添加库存'" :visible.sync="visible" width="600px" @close="closeDialog"> | |
| 3 | + <el-form ref="form" :model="form" :rules="rules" label-width="100px"> | |
| 4 | + <el-row :gutter="20"> | |
| 5 | + <el-col :span="12"> | |
| 6 | + <el-form-item label="产品名称" prop="productName"> | |
| 7 | + <el-input v-model="form.productName" placeholder="请输入产品名称" /> | |
| 8 | + </el-form-item> | |
| 9 | + </el-col> | |
| 10 | + <el-col :span="12"> | |
| 11 | + <el-form-item label="产品分类" prop="productCategory"> | |
| 12 | + <el-input v-model="form.productCategory" placeholder="请输入产品分类" /> | |
| 13 | + </el-form-item> | |
| 14 | + </el-col> | |
| 15 | + </el-row> | |
| 16 | + | |
| 17 | + <el-row :gutter="20"> | |
| 18 | + <el-col :span="12"> | |
| 19 | + <el-form-item label="单价" prop="price"> | |
| 20 | + <el-input-number v-model="form.price" :min="0" :precision="2" style="width: 100%" | |
| 21 | + placeholder="请输入单价" /> | |
| 22 | + </el-form-item> | |
| 23 | + </el-col> | |
| 24 | + <el-col :span="12"> | |
| 25 | + <el-form-item label="库存数量" prop="quantity"> | |
| 26 | + <el-input-number v-model="form.quantity" :min="0" style="width: 100%" placeholder="请输入库存数量" /> | |
| 27 | + </el-form-item> | |
| 28 | + </el-col> | |
| 29 | + </el-row> | |
| 30 | + | |
| 31 | + <el-row :gutter="20"> | |
| 32 | + <el-col :span="12"> | |
| 33 | + <el-form-item label="标准单位" prop="standardUnit"> | |
| 34 | + <el-input v-model="form.standardUnit" placeholder="请输入标准单位" /> | |
| 35 | + </el-form-item> | |
| 36 | + </el-col> | |
| 37 | + <el-col :span="12"> | |
| 38 | + <el-form-item label="负责部门" prop="departmentId"> | |
| 39 | + <el-select v-model="form.departmentId" placeholder="请选择负责部门" style="width: 100%" clearable> | |
| 40 | + <el-option v-for="dept in departmentList" :key="dept.id" :label="dept.name" | |
| 41 | + :value="dept.id"> | |
| 42 | + </el-option> | |
| 43 | + </el-select> | |
| 44 | + </el-form-item> | |
| 45 | + </el-col> | |
| 46 | + </el-row> | |
| 47 | + | |
| 48 | + <el-form-item label="备注"> | |
| 49 | + <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注信息" /> | |
| 50 | + </el-form-item> | |
| 51 | + </el-form> | |
| 52 | + | |
| 53 | + <div slot="footer" class="dialog-footer"> | |
| 54 | + <el-button @click="closeDialog">取消</el-button> | |
| 55 | + <el-button type="primary" @click="submitForm" :loading="submitLoading">确定</el-button> | |
| 56 | + </div> | |
| 57 | + </el-dialog> | |
| 58 | +</template> | |
| 59 | + | |
| 60 | +<script> | |
| 61 | +import { createInventory, updateInventory, getInventoryInfo } from '@/api/extend/lqInventory' | |
| 62 | +import { getUserList } from '@/api/permission/user' | |
| 63 | + | |
| 64 | +export default { | |
| 65 | + name: 'InventoryForm', | |
| 66 | + data() { | |
| 67 | + return { | |
| 68 | + visible: false, | |
| 69 | + submitLoading: false, | |
| 70 | + isEdit: false, | |
| 71 | + departmentList: [], // 部门列表 | |
| 72 | + form: { | |
| 73 | + id: '', | |
| 74 | + productName: '', | |
| 75 | + productCategory: '', | |
| 76 | + price: 0, | |
| 77 | + quantity: 0, | |
| 78 | + standardUnit: '', | |
| 79 | + departmentId: '', | |
| 80 | + departmentName: '', | |
| 81 | + remark: '' | |
| 82 | + }, | |
| 83 | + rules: { | |
| 84 | + productName: [ | |
| 85 | + { required: true, message: '请输入产品名称', trigger: 'blur' } | |
| 86 | + ], | |
| 87 | + productCategory: [ | |
| 88 | + { required: true, message: '请输入产品分类', trigger: 'blur' } | |
| 89 | + ], | |
| 90 | + price: [ | |
| 91 | + { required: true, message: '请输入单价', trigger: 'blur' } | |
| 92 | + ], | |
| 93 | + quantity: [ | |
| 94 | + { required: true, message: '请输入库存数量', trigger: 'blur' } | |
| 95 | + ], | |
| 96 | + standardUnit: [ | |
| 97 | + { required: true, message: '请输入标准单位', trigger: 'blur' } | |
| 98 | + ], | |
| 99 | + departmentId: [ | |
| 100 | + { required: true, message: '请选择负责部门', trigger: 'change' } | |
| 101 | + ] | |
| 102 | + } | |
| 103 | + } | |
| 104 | + }, | |
| 105 | + methods: { | |
| 106 | + init(id = '') { | |
| 107 | + this.visible = true | |
| 108 | + this.isEdit = !!id | |
| 109 | + this.getDepartmentList() | |
| 110 | + if (this.isEdit) { | |
| 111 | + this.getInventoryInfo(id) | |
| 112 | + } else { | |
| 113 | + this.resetForm() | |
| 114 | + } | |
| 115 | + }, | |
| 116 | + | |
| 117 | + // 获取部门列表 | |
| 118 | + getDepartmentList() { | |
| 119 | + const params = { | |
| 120 | + currentPage: 1, | |
| 121 | + pageSize: 1000, // 获取所有部门 | |
| 122 | + enabledMark: 1 // 只获取启用的部门 | |
| 123 | + } | |
| 124 | + | |
| 125 | + getUserList(params).then(response => { | |
| 126 | + if (response.code === 200) { | |
| 127 | + // 将用户列表转换为部门列表格式 | |
| 128 | + this.departmentList = response.data.list.map(user => ({ | |
| 129 | + id: user.id, | |
| 130 | + name: user.realName | |
| 131 | + })) | |
| 132 | + } else { | |
| 133 | + this.$message.error(response.msg || '获取部门列表失败') | |
| 134 | + } | |
| 135 | + }).catch(() => { | |
| 136 | + this.$message.error('获取部门列表失败') | |
| 137 | + }) | |
| 138 | + }, | |
| 139 | + | |
| 140 | + // 获取库存详情 | |
| 141 | + getInventoryInfo(id) { | |
| 142 | + getInventoryInfo(id).then(response => { | |
| 143 | + if (response.code === 200) { | |
| 144 | + this.form = { | |
| 145 | + id: response.data.id, | |
| 146 | + productName: response.data.productName, | |
| 147 | + productCategory: response.data.productCategory, | |
| 148 | + price: response.data.price, | |
| 149 | + quantity: response.data.quantity, | |
| 150 | + standardUnit: response.data.standardUnit, | |
| 151 | + departmentId: response.data.departmentId, | |
| 152 | + departmentName: response.data.departmentName, | |
| 153 | + remark: response.data.remark || '' | |
| 154 | + } | |
| 155 | + } else { | |
| 156 | + this.$message.error(response.msg || '获取库存详情失败') | |
| 157 | + } | |
| 158 | + }) | |
| 159 | + }, | |
| 160 | + | |
| 161 | + | |
| 162 | + resetForm() { | |
| 163 | + this.form = { | |
| 164 | + id: '', | |
| 165 | + productName: '', | |
| 166 | + productCategory: '', | |
| 167 | + price: 0, | |
| 168 | + quantity: 0, | |
| 169 | + standardUnit: '', | |
| 170 | + departmentId: '', | |
| 171 | + departmentName: '', | |
| 172 | + remark: '' | |
| 173 | + } | |
| 174 | + this.$nextTick(() => { | |
| 175 | + if (this.$refs.form) { | |
| 176 | + this.$refs.form.clearValidate() | |
| 177 | + } | |
| 178 | + }) | |
| 179 | + }, | |
| 180 | + | |
| 181 | + // 提交表单 | |
| 182 | + submitForm() { | |
| 183 | + this.$refs.form.validate((valid) => { | |
| 184 | + if (valid) { | |
| 185 | + this.submitLoading = true | |
| 186 | + const apiMethod = this.isEdit ? updateInventory : createInventory | |
| 187 | + apiMethod(this.form).then(response => { | |
| 188 | + if (response.code === 200) { | |
| 189 | + this.$message.success(this.isEdit ? '更新成功' : '创建成功') | |
| 190 | + this.closeDialog() | |
| 191 | + this.$emit('refreshDataList') | |
| 192 | + } else { | |
| 193 | + this.$message.error(response.msg || (this.isEdit ? '更新失败' : '创建失败')) | |
| 194 | + } | |
| 195 | + this.submitLoading = false | |
| 196 | + }).catch(() => { | |
| 197 | + this.submitLoading = false | |
| 198 | + }) | |
| 199 | + } | |
| 200 | + }) | |
| 201 | + }, | |
| 202 | + | |
| 203 | + // 关闭弹窗 | |
| 204 | + closeDialog() { | |
| 205 | + this.visible = false | |
| 206 | + this.resetForm() | |
| 207 | + } | |
| 208 | + } | |
| 209 | +} | |
| 210 | +</script> | |
| 211 | + | |
| 212 | +<style scoped> | |
| 213 | +.dialog-footer { | |
| 214 | + text-align: right; | |
| 215 | +} | |
| 216 | +</style> | ... | ... |
antis-ncc-admin/src/views/lqInventory/InventoryInfoDialog.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog title="库存详情" :visible.sync="visible" width="800px" @close="closeDialog"> | |
| 3 | + <div class="inventory-info-container"> | |
| 4 | + <el-row :gutter="20"> | |
| 5 | + <el-col :span="12"> | |
| 6 | + <div class="info-item"> | |
| 7 | + <label>产品名称:</label> | |
| 8 | + <span>{{ inventoryInfo.productName || '无' }}</span> | |
| 9 | + </div> | |
| 10 | + </el-col> | |
| 11 | + <el-col :span="12"> | |
| 12 | + <div class="info-item"> | |
| 13 | + <label>产品分类:</label> | |
| 14 | + <span>{{ inventoryInfo.productCategory || '无' }}</span> | |
| 15 | + </div> | |
| 16 | + </el-col> | |
| 17 | + </el-row> | |
| 18 | + | |
| 19 | + <el-row :gutter="20"> | |
| 20 | + <el-col :span="12"> | |
| 21 | + <div class="info-item"> | |
| 22 | + <label>单价:</label> | |
| 23 | + <span class="price">{{ formatMoney(inventoryInfo.price) }}</span> | |
| 24 | + </div> | |
| 25 | + </el-col> | |
| 26 | + <el-col :span="12"> | |
| 27 | + <div class="info-item"> | |
| 28 | + <label>库存数量:</label> | |
| 29 | + <span :class="inventoryInfo.quantity <= 10 ? 'text-danger' : ''">{{ inventoryInfo.quantity || 0 | |
| 30 | + }}</span> | |
| 31 | + </div> | |
| 32 | + </el-col> | |
| 33 | + </el-row> | |
| 34 | + | |
| 35 | + <el-row :gutter="20"> | |
| 36 | + <el-col :span="12"> | |
| 37 | + <div class="info-item"> | |
| 38 | + <label>标准单位:</label> | |
| 39 | + <span>{{ inventoryInfo.standardUnit || '无' }}</span> | |
| 40 | + </div> | |
| 41 | + </el-col> | |
| 42 | + <el-col :span="12"> | |
| 43 | + <div class="info-item"> | |
| 44 | + <label>总价值:</label> | |
| 45 | + <span class="price">{{ formatMoney(inventoryInfo.totalValue) }}</span> | |
| 46 | + </div> | |
| 47 | + </el-col> | |
| 48 | + </el-row> | |
| 49 | + | |
| 50 | + <el-row :gutter="20"> | |
| 51 | + <el-col :span="12"> | |
| 52 | + <div class="info-item"> | |
| 53 | + <label>负责部门:</label> | |
| 54 | + <span>{{ inventoryInfo.departmentName || '无' }}</span> | |
| 55 | + </div> | |
| 56 | + </el-col> | |
| 57 | + <el-col :span="12"> | |
| 58 | + <div class="info-item"> | |
| 59 | + <label>状态:</label> | |
| 60 | + <el-tag :type="inventoryInfo.isEffective === 1 ? 'success' : 'danger'"> | |
| 61 | + {{ inventoryInfo.isEffective === 1 ? '有效' : '无效' }} | |
| 62 | + </el-tag> | |
| 63 | + </div> | |
| 64 | + </el-col> | |
| 65 | + </el-row> | |
| 66 | + | |
| 67 | + <el-row :gutter="20"> | |
| 68 | + <el-col :span="12"> | |
| 69 | + <div class="info-item"> | |
| 70 | + <label>创建人:</label> | |
| 71 | + <span>{{ inventoryInfo.createUserName || '无' }}</span> | |
| 72 | + </div> | |
| 73 | + </el-col> | |
| 74 | + <el-col :span="12"> | |
| 75 | + <div class="info-item"> | |
| 76 | + <label>创建时间:</label> | |
| 77 | + <span>{{ inventoryInfo.createTime | dateTimeFormat }}</span> | |
| 78 | + </div> | |
| 79 | + </el-col> | |
| 80 | + </el-row> | |
| 81 | + | |
| 82 | + <el-row :gutter="20" v-if="inventoryInfo.updateTime"> | |
| 83 | + <el-col :span="12"> | |
| 84 | + <div class="info-item"> | |
| 85 | + <label>更新人:</label> | |
| 86 | + <span>{{ inventoryInfo.updateUserName || '无' }}</span> | |
| 87 | + </div> | |
| 88 | + </el-col> | |
| 89 | + <el-col :span="12"> | |
| 90 | + <div class="info-item"> | |
| 91 | + <label>更新时间:</label> | |
| 92 | + <span>{{ inventoryInfo.updateTime | dateTimeFormat }}</span> | |
| 93 | + </div> | |
| 94 | + </el-col> | |
| 95 | + </el-row> | |
| 96 | + | |
| 97 | + <el-row v-if="inventoryInfo.remark"> | |
| 98 | + <el-col :span="24"> | |
| 99 | + <div class="info-item"> | |
| 100 | + <label>备注:</label> | |
| 101 | + <span>{{ inventoryInfo.remark }}</span> | |
| 102 | + </div> | |
| 103 | + </el-col> | |
| 104 | + </el-row> | |
| 105 | + </div> | |
| 106 | + | |
| 107 | + <div slot="footer" class="dialog-footer"> | |
| 108 | + <el-button @click="closeDialog">关闭</el-button> | |
| 109 | + </div> | |
| 110 | + </el-dialog> | |
| 111 | +</template> | |
| 112 | + | |
| 113 | +<script> | |
| 114 | +import { getInventoryInfo } from '@/api/extend/lqInventory' | |
| 115 | + | |
| 116 | +export default { | |
| 117 | + name: 'InventoryInfoDialog', | |
| 118 | + data() { | |
| 119 | + return { | |
| 120 | + visible: false, | |
| 121 | + inventoryInfo: {} | |
| 122 | + } | |
| 123 | + }, | |
| 124 | + methods: { | |
| 125 | + init(id) { | |
| 126 | + this.visible = true | |
| 127 | + this.getInventoryInfo(id) | |
| 128 | + }, | |
| 129 | + | |
| 130 | + // 获取库存详情 | |
| 131 | + getInventoryInfo(id) { | |
| 132 | + getInventoryInfo(id).then(response => { | |
| 133 | + if (response.code === 200) { | |
| 134 | + this.inventoryInfo = response.data | |
| 135 | + } else { | |
| 136 | + this.$message.error(response.msg || '获取库存详情失败') | |
| 137 | + } | |
| 138 | + }) | |
| 139 | + }, | |
| 140 | + | |
| 141 | + // 格式化金额 | |
| 142 | + formatMoney(value) { | |
| 143 | + if (value === null || value === undefined || value === '') { | |
| 144 | + return '0.00' | |
| 145 | + } | |
| 146 | + return Number(value).toFixed(2) | |
| 147 | + }, | |
| 148 | + | |
| 149 | + // 关闭弹窗 | |
| 150 | + closeDialog() { | |
| 151 | + this.visible = false | |
| 152 | + this.inventoryInfo = {} | |
| 153 | + } | |
| 154 | + } | |
| 155 | +} | |
| 156 | +</script> | |
| 157 | + | |
| 158 | +<style scoped> | |
| 159 | +.inventory-info-container { | |
| 160 | + padding: 20px; | |
| 161 | +} | |
| 162 | + | |
| 163 | +.info-item { | |
| 164 | + margin-bottom: 20px; | |
| 165 | + display: flex; | |
| 166 | + align-items: center; | |
| 167 | +} | |
| 168 | + | |
| 169 | +.info-item label { | |
| 170 | + font-weight: bold; | |
| 171 | + color: #606266; | |
| 172 | + min-width: 100px; | |
| 173 | + margin-right: 10px; | |
| 174 | +} | |
| 175 | + | |
| 176 | +.info-item span { | |
| 177 | + color: #303133; | |
| 178 | +} | |
| 179 | + | |
| 180 | +.price { | |
| 181 | + color: #e6a23c; | |
| 182 | + font-weight: bold; | |
| 183 | +} | |
| 184 | + | |
| 185 | +.text-danger { | |
| 186 | + color: #f56c6c; | |
| 187 | + font-weight: bold; | |
| 188 | +} | |
| 189 | + | |
| 190 | +.dialog-footer { | |
| 191 | + text-align: right; | |
| 192 | +} | |
| 193 | +</style> | ... | ... |
antis-ncc-admin/src/views/lqInventory/UsageRecordDialog.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog title="库存使用记录管理" :visible.sync="visible" width="1400px" @close="closeDialog"> | |
| 3 | + <div class="usage-record-container"> | |
| 4 | + <!-- 搜索区域 --> | |
| 5 | + <div class="search-section"> | |
| 6 | + <el-form :model="query" :inline="true" class="search-form"> | |
| 7 | + <el-form-item label="产品名称"> | |
| 8 | + <el-input v-model="query.productName" placeholder="请输入产品名称" clearable size="small" /> | |
| 9 | + </el-form-item> | |
| 10 | + <el-form-item label="产品分类"> | |
| 11 | + <el-input v-model="query.productCategory" placeholder="请输入产品分类" clearable size="small" /> | |
| 12 | + </el-form-item> | |
| 13 | + <el-form-item label="门店"> | |
| 14 | + <el-input v-model="query.storeName" placeholder="请输入门店名称" clearable size="small" /> | |
| 15 | + </el-form-item> | |
| 16 | + <el-form-item label="使用时间"> | |
| 17 | + <el-date-picker v-model="query.usageTimeRange" type="daterange" range-separator="至" | |
| 18 | + start-placeholder="开始日期" end-placeholder="结束日期" value-format="yyyy-MM-dd" size="small"> | |
| 19 | + </el-date-picker> | |
| 20 | + </el-form-item> | |
| 21 | + <el-form-item> | |
| 22 | + <el-button type="primary" size="small" @click="search">搜索</el-button> | |
| 23 | + <el-button size="small" @click="reset">重置</el-button> | |
| 24 | + <el-button type="success" size="small" icon="el-icon-plus" | |
| 25 | + @click="addUsageRecord">添加使用记录</el-button> | |
| 26 | + </el-form-item> | |
| 27 | + </el-form> | |
| 28 | + </div> | |
| 29 | + | |
| 30 | + <!-- 使用记录列表 --> | |
| 31 | + <div class="table-section"> | |
| 32 | + <el-table :data="usageList" v-loading="loading" border stripe style="width: 100%"> | |
| 33 | + <el-table-column prop="productName" label="产品名称" width="150" fixed="left" /> | |
| 34 | + <el-table-column prop="productCategory" label="产品分类" width="120" /> | |
| 35 | + <el-table-column prop="productPrice" label="单价" width="100" align="right"> | |
| 36 | + <template slot-scope="scope"> | |
| 37 | + {{ formatMoney(scope.row.productPrice) }} | |
| 38 | + </template> | |
| 39 | + </el-table-column> | |
| 40 | + <el-table-column prop="usageQuantity" label="使用数量" width="100" align="right" /> | |
| 41 | + <el-table-column prop="storeName" label="使用门店" width="120" /> | |
| 42 | + <el-table-column prop="usageTime" label="使用时间" width="120"> | |
| 43 | + <template slot-scope="scope"> | |
| 44 | + {{ scope.row.usageTime | dateFormat }} | |
| 45 | + </template> | |
| 46 | + </el-table-column> | |
| 47 | + <el-table-column prop="relatedConsumeId" label="关联消费ID" width="120" /> | |
| 48 | + <el-table-column prop="createUserName" label="创建人" width="100" /> | |
| 49 | + <el-table-column prop="createTime" label="创建时间" width="160"> | |
| 50 | + <template slot-scope="scope"> | |
| 51 | + {{ scope.row.createTime | dateTimeFormat }} | |
| 52 | + </template> | |
| 53 | + </el-table-column> | |
| 54 | + <el-table-column prop="isEffective" label="状态" width="80" align="center"> | |
| 55 | + <template slot-scope="scope"> | |
| 56 | + <el-tag :type="scope.row.isEffective === 1 ? 'success' : 'danger'" size="small"> | |
| 57 | + {{ scope.row.isEffective === 1 ? '有效' : '无效' }} | |
| 58 | + </el-tag> | |
| 59 | + </template> | |
| 60 | + </el-table-column> | |
| 61 | + <el-table-column label="操作" width="120" fixed="right"> | |
| 62 | + <template slot-scope="scope"> | |
| 63 | + <el-button v-if="scope.row.isEffective === 1" type="text" size="small" | |
| 64 | + @click="cancelUsageRecord(scope.row)">作废</el-button> | |
| 65 | + </template> | |
| 66 | + </el-table-column> | |
| 67 | + </el-table> | |
| 68 | + | |
| 69 | + <!-- 分页 --> | |
| 70 | + <div class="pagination-section"> | |
| 71 | + <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" | |
| 72 | + :current-page="pagination.pageIndex" :page-sizes="[10, 20, 50, 100]" | |
| 73 | + :page-size="pagination.pageSize" layout="total, sizes, prev, pager, next, jumper" | |
| 74 | + :total="pagination.total"> | |
| 75 | + </el-pagination> | |
| 76 | + </div> | |
| 77 | + </div> | |
| 78 | + </div> | |
| 79 | + | |
| 80 | + <!-- 添加使用记录弹窗 --> | |
| 81 | + <AddUsageRecordForm v-if="addUsageRecordVisible" ref="AddUsageRecordForm" @refreshDataList="getUsageList" /> | |
| 82 | + | |
| 83 | + <div slot="footer" class="dialog-footer"> | |
| 84 | + <el-button @click="closeDialog">关闭</el-button> | |
| 85 | + </div> | |
| 86 | + </el-dialog> | |
| 87 | +</template> | |
| 88 | + | |
| 89 | +<script> | |
| 90 | +import { getInventoryUsageList, cancelInventoryUsage } from '@/api/extend/lqInventory' | |
| 91 | +import AddUsageRecordForm from './AddUsageRecordForm' | |
| 92 | + | |
| 93 | +export default { | |
| 94 | + name: 'UsageRecordDialog', | |
| 95 | + components: { | |
| 96 | + AddUsageRecordForm | |
| 97 | + }, | |
| 98 | + data() { | |
| 99 | + return { | |
| 100 | + visible: false, | |
| 101 | + loading: false, | |
| 102 | + productId: '', // 特定产品ID,为空时显示所有 | |
| 103 | + productName: '', // 特定产品名称 | |
| 104 | + usageList: [], | |
| 105 | + query: { | |
| 106 | + productName: '', | |
| 107 | + productCategory: '', | |
| 108 | + storeName: '', | |
| 109 | + usageTimeRange: [], | |
| 110 | + productId: '', | |
| 111 | + storeId: '', | |
| 112 | + relatedConsumeId: '', | |
| 113 | + currentPage: 1, | |
| 114 | + pageSize: 20 | |
| 115 | + }, | |
| 116 | + pagination: { | |
| 117 | + pageIndex: 1, | |
| 118 | + pageSize: 20, | |
| 119 | + total: 0 | |
| 120 | + }, | |
| 121 | + addUsageRecordVisible: false | |
| 122 | + } | |
| 123 | + }, | |
| 124 | + methods: { | |
| 125 | + init(productId = '', productName = '') { | |
| 126 | + this.visible = true | |
| 127 | + this.productId = productId | |
| 128 | + this.productName = productName | |
| 129 | + this.query.productId = productId | |
| 130 | + if (productName) { | |
| 131 | + this.query.productName = productName | |
| 132 | + } | |
| 133 | + this.getUsageList() | |
| 134 | + }, | |
| 135 | + | |
| 136 | + // 获取使用记录列表 | |
| 137 | + getUsageList() { | |
| 138 | + this.loading = true | |
| 139 | + const params = { | |
| 140 | + ...this.query, | |
| 141 | + currentPage: this.pagination.pageIndex, | |
| 142 | + pageSize: this.pagination.pageSize | |
| 143 | + } | |
| 144 | + | |
| 145 | + // 处理日期范围 | |
| 146 | + if (this.query.usageTimeRange && this.query.usageTimeRange.length === 2) { | |
| 147 | + params.usageStartTime = this.query.usageTimeRange[0] | |
| 148 | + params.usageEndTime = this.query.usageTimeRange[1] | |
| 149 | + } | |
| 150 | + | |
| 151 | + getInventoryUsageList(params).then(response => { | |
| 152 | + if (response.code === 200) { | |
| 153 | + this.usageList = response.data.list || [] | |
| 154 | + this.pagination.total = (response.data.pagination && response.data.pagination.total) || 0 | |
| 155 | + } else { | |
| 156 | + this.$message.error(response.msg || '获取使用记录失败') | |
| 157 | + } | |
| 158 | + this.loading = false | |
| 159 | + }).catch(() => { | |
| 160 | + this.loading = false | |
| 161 | + }) | |
| 162 | + }, | |
| 163 | + | |
| 164 | + // 搜索 | |
| 165 | + search() { | |
| 166 | + this.pagination.pageIndex = 1 | |
| 167 | + this.getUsageList() | |
| 168 | + }, | |
| 169 | + | |
| 170 | + // 重置 | |
| 171 | + reset() { | |
| 172 | + this.query = { | |
| 173 | + productName: this.productName || '', | |
| 174 | + productCategory: '', | |
| 175 | + storeName: '', | |
| 176 | + usageTimeRange: [], | |
| 177 | + productId: this.productId, | |
| 178 | + storeId: '', | |
| 179 | + relatedConsumeId: '', | |
| 180 | + currentPage: 1, | |
| 181 | + pageSize: 20 | |
| 182 | + } | |
| 183 | + this.pagination.pageIndex = 1 | |
| 184 | + this.getUsageList() | |
| 185 | + }, | |
| 186 | + | |
| 187 | + // 分页大小改变 | |
| 188 | + handleSizeChange(val) { | |
| 189 | + this.pagination.pageSize = val | |
| 190 | + this.pagination.pageIndex = 1 | |
| 191 | + this.getUsageList() | |
| 192 | + }, | |
| 193 | + | |
| 194 | + // 当前页改变 | |
| 195 | + handleCurrentChange(val) { | |
| 196 | + this.pagination.pageIndex = val | |
| 197 | + this.getUsageList() | |
| 198 | + }, | |
| 199 | + | |
| 200 | + // 添加使用记录 | |
| 201 | + addUsageRecord() { | |
| 202 | + this.addUsageRecordVisible = true | |
| 203 | + this.$nextTick(() => { | |
| 204 | + this.$refs.AddUsageRecordForm.init(this.productId, this.productName) | |
| 205 | + }) | |
| 206 | + }, | |
| 207 | + | |
| 208 | + // 作废使用记录 | |
| 209 | + cancelUsageRecord(row) { | |
| 210 | + this.$confirm('确定要作废这条使用记录吗?作废后库存数量将恢复。', '提示', { | |
| 211 | + confirmButtonText: '确定', | |
| 212 | + cancelButtonText: '取消', | |
| 213 | + type: 'warning' | |
| 214 | + }).then(() => { | |
| 215 | + cancelInventoryUsage(row.Id).then(response => { | |
| 216 | + if (response.code === 200) { | |
| 217 | + this.$message.success('作废成功') | |
| 218 | + this.getUsageList() | |
| 219 | + } else { | |
| 220 | + this.$message.error(response.msg || '作废失败') | |
| 221 | + } | |
| 222 | + }) | |
| 223 | + }) | |
| 224 | + }, | |
| 225 | + | |
| 226 | + // 格式化金额 | |
| 227 | + formatMoney(value) { | |
| 228 | + if (value === null || value === undefined || value === '') { | |
| 229 | + return '0.00' | |
| 230 | + } | |
| 231 | + return Number(value).toFixed(2) | |
| 232 | + }, | |
| 233 | + | |
| 234 | + // 关闭弹窗 | |
| 235 | + closeDialog() { | |
| 236 | + this.visible = false | |
| 237 | + this.usageList = [] | |
| 238 | + this.query = { | |
| 239 | + productName: '', | |
| 240 | + productCategory: '', | |
| 241 | + storeName: '', | |
| 242 | + usageTimeRange: [], | |
| 243 | + productId: '', | |
| 244 | + storeId: '', | |
| 245 | + relatedConsumeId: '', | |
| 246 | + currentPage: 1, | |
| 247 | + pageSize: 20 | |
| 248 | + } | |
| 249 | + this.pagination = { | |
| 250 | + pageIndex: 1, | |
| 251 | + pageSize: 20, | |
| 252 | + total: 0 | |
| 253 | + } | |
| 254 | + } | |
| 255 | + } | |
| 256 | +} | |
| 257 | +</script> | |
| 258 | + | |
| 259 | +<style scoped> | |
| 260 | +.usage-record-container { | |
| 261 | + padding: 20px; | |
| 262 | +} | |
| 263 | + | |
| 264 | +.search-section { | |
| 265 | + margin-bottom: 20px; | |
| 266 | + padding: 15px; | |
| 267 | + background: #f5f7fa; | |
| 268 | + border-radius: 4px; | |
| 269 | +} | |
| 270 | + | |
| 271 | +.search-form .el-form-item { | |
| 272 | + margin-bottom: 10px; | |
| 273 | +} | |
| 274 | + | |
| 275 | +.table-section { | |
| 276 | + margin-bottom: 20px; | |
| 277 | +} | |
| 278 | + | |
| 279 | +.pagination-section { | |
| 280 | + text-align: right; | |
| 281 | + margin-top: 20px; | |
| 282 | +} | |
| 283 | + | |
| 284 | +.dialog-footer { | |
| 285 | + text-align: right; | |
| 286 | +} | |
| 287 | +</style> | ... | ... |
antis-ncc-admin/src/views/lqInventory/index.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="产品名称"> | |
| 9 | + <el-input v-model="query.productName" placeholder="请输入产品名称" clearable /> | |
| 10 | + </el-form-item> | |
| 11 | + </el-col> | |
| 12 | + <el-col :span="6"> | |
| 13 | + <el-form-item label="产品分类"> | |
| 14 | + <el-input v-model="query.productCategory" placeholder="请输入产品分类" clearable /> | |
| 15 | + </el-form-item> | |
| 16 | + </el-col> | |
| 17 | + <el-col :span="6"> | |
| 18 | + <el-form-item label="负责部门"> | |
| 19 | + <el-select v-model="query.departmentId" placeholder="请选择负责部门" clearable style="width: 100%"> | |
| 20 | + <el-option v-for="dept in departmentList" :key="dept.id" :label="dept.name" | |
| 21 | + :value="dept.id"> | |
| 22 | + </el-option> | |
| 23 | + </el-select> | |
| 24 | + </el-form-item> | |
| 25 | + </el-col> | |
| 26 | + <el-col :span="6"> | |
| 27 | + <el-form-item> | |
| 28 | + <el-button type="primary" icon="el-icon-search" @click="search()">查询</el-button> | |
| 29 | + <el-button icon="el-icon-refresh-right" @click="reset()">重置</el-button> | |
| 30 | + </el-form-item> | |
| 31 | + </el-col> | |
| 32 | + </el-form> | |
| 33 | + </el-row> | |
| 34 | + | |
| 35 | + <!-- 主要内容区域 --> | |
| 36 | + <div class="NCC-common-layout-main NCC-flex-main"> | |
| 37 | + <!-- 操作按钮区域 --> | |
| 38 | + <div class="NCC-common-head"> | |
| 39 | + <div> | |
| 40 | + <el-button type="primary" icon="el-icon-plus" @click="addInventoryHandle()">添加库存</el-button> | |
| 41 | + <el-button type="success" icon="el-icon-edit" @click="editInventoryHandle()" | |
| 42 | + :disabled="selectedIds.length !== 1">编辑库存</el-button> | |
| 43 | + <el-button type="warning" icon="el-icon-s-operation" | |
| 44 | + @click="usageRecordHandle()">使用记录</el-button> | |
| 45 | + <el-button type="text" icon="el-icon-download" @click="exportData()">导出</el-button> | |
| 46 | + </div> | |
| 47 | + <div class="NCC-common-head-right"> | |
| 48 | + <el-tooltip effect="dark" content="刷新" placement="top"> | |
| 49 | + <el-link icon="icon-ym icon-ym-Refresh NCC-common-head-icon" :underline="false" | |
| 50 | + @click="reset()" /> | |
| 51 | + </el-tooltip> | |
| 52 | + <screenfull isContainer /> | |
| 53 | + </div> | |
| 54 | + </div> | |
| 55 | + | |
| 56 | + <!-- 库存列表表格 --> | |
| 57 | + <NCC-table v-loading="listLoading" :data="list" has-c @selection-change="handleSelectionChange"> | |
| 58 | + <el-table-column prop="productName" label="产品名称" align="left" show-overflow-tooltip /> | |
| 59 | + <el-table-column prop="productCategory" label="产品分类" align="left" width="120" /> | |
| 60 | + <el-table-column prop="price" label="单价" align="right" width="100"> | |
| 61 | + <template slot-scope="scope"> | |
| 62 | + {{ formatMoney(scope.row.price) }} | |
| 63 | + </template> | |
| 64 | + </el-table-column> | |
| 65 | + <el-table-column prop="quantity" label="库存数量" align="right" width="100"> | |
| 66 | + <template slot-scope="scope"> | |
| 67 | + <span :class="scope.row.quantity <= 10 ? 'text-danger' : ''">{{ scope.row.quantity }}</span> | |
| 68 | + </template> | |
| 69 | + </el-table-column> | |
| 70 | + <el-table-column prop="standardUnit" label="单位" align="center" width="80" /> | |
| 71 | + <el-table-column prop="totalValue" label="总价值" align="right" width="120"> | |
| 72 | + <template slot-scope="scope"> | |
| 73 | + {{ formatMoney(scope.row.totalValue) }} | |
| 74 | + </template> | |
| 75 | + </el-table-column> | |
| 76 | + <el-table-column prop="departmentName" label="负责部门" align="left" width="120" /> | |
| 77 | + <el-table-column prop="createTime" label="创建时间" align="center" width="160"> | |
| 78 | + <template slot-scope="scope"> | |
| 79 | + {{ scope.row.createTime | dateTimeFormat }} | |
| 80 | + </template> | |
| 81 | + </el-table-column> | |
| 82 | + <el-table-column label="操作" align="center" width="200" fixed="right"> | |
| 83 | + <template slot-scope="scope"> | |
| 84 | + <el-button type="text" @click="viewInventoryInfo(scope.row)">查看详情</el-button> | |
| 85 | + <el-button type="text" @click="editInventory(scope.row)">编辑</el-button> | |
| 86 | + <el-button type="text" @click="viewUsageRecords(scope.row)">使用记录</el-button> | |
| 87 | + </template> | |
| 88 | + </el-table-column> | |
| 89 | + </NCC-table> | |
| 90 | + | |
| 91 | + <!-- 分页组件 --> | |
| 92 | + <pagination v-show="total > 0" :total="total" :page.sync="query.currentPage" | |
| 93 | + :limit.sync="query.pageSize" @pagination="getList" /> | |
| 94 | + </div> | |
| 95 | + </div> | |
| 96 | + | |
| 97 | + <!-- 添加/编辑库存弹窗 --> | |
| 98 | + <InventoryForm v-if="inventoryFormVisible" ref="InventoryForm" @refreshDataList="getList" /> | |
| 99 | + | |
| 100 | + <!-- 库存详情弹窗 --> | |
| 101 | + <InventoryInfoDialog v-if="inventoryInfoVisible" ref="InventoryInfoDialog" /> | |
| 102 | + | |
| 103 | + <!-- 使用记录弹窗 --> | |
| 104 | + <UsageRecordDialog v-if="usageRecordVisible" ref="UsageRecordDialog" /> | |
| 105 | + </div> | |
| 106 | +</template> | |
| 107 | + | |
| 108 | +<script> | |
| 109 | +import { getInventoryList } from '@/api/extend/lqInventory' | |
| 110 | +import { getUserList } from '@/api/permission/user' | |
| 111 | +import InventoryForm from './InventoryForm' | |
| 112 | +import InventoryInfoDialog from './InventoryInfoDialog' | |
| 113 | +import UsageRecordDialog from './UsageRecordDialog' | |
| 114 | +import Pagination from '@/components/Pagination' | |
| 115 | + | |
| 116 | +export default { | |
| 117 | + name: 'LqInventory', | |
| 118 | + components: { | |
| 119 | + InventoryForm, | |
| 120 | + InventoryInfoDialog, | |
| 121 | + UsageRecordDialog, | |
| 122 | + Pagination | |
| 123 | + }, | |
| 124 | + data() { | |
| 125 | + return { | |
| 126 | + query: { | |
| 127 | + productName: '', | |
| 128 | + productCategory: '', | |
| 129 | + departmentId: '', | |
| 130 | + currentPage: 1, | |
| 131 | + pageSize: 20 | |
| 132 | + }, | |
| 133 | + list: [], | |
| 134 | + total: 0, | |
| 135 | + listLoading: true, | |
| 136 | + inventoryFormVisible: false, | |
| 137 | + inventoryInfoVisible: false, | |
| 138 | + usageRecordVisible: false, | |
| 139 | + selectedIds: [], | |
| 140 | + departmentList: [] // 部门列表 | |
| 141 | + } | |
| 142 | + }, | |
| 143 | + created() { | |
| 144 | + this.getDepartmentList() | |
| 145 | + this.getList() | |
| 146 | + }, | |
| 147 | + methods: { | |
| 148 | + // 获取部门列表 | |
| 149 | + getDepartmentList() { | |
| 150 | + const params = { | |
| 151 | + currentPage: 1, | |
| 152 | + pageSize: 1000, // 获取所有部门 | |
| 153 | + enabledMark: 1 // 只获取启用的部门 | |
| 154 | + } | |
| 155 | + | |
| 156 | + getUserList(params).then(response => { | |
| 157 | + if (response.code === 200) { | |
| 158 | + // 将用户列表转换为部门列表格式 | |
| 159 | + this.departmentList = response.data.list.map(user => ({ | |
| 160 | + id: user.id, | |
| 161 | + name: user.realName | |
| 162 | + })) | |
| 163 | + } else { | |
| 164 | + this.$message.error(response.msg || '获取部门列表失败') | |
| 165 | + } | |
| 166 | + }).catch(() => { | |
| 167 | + this.$message.error('获取部门列表失败') | |
| 168 | + }) | |
| 169 | + }, | |
| 170 | + | |
| 171 | + // 获取库存列表 | |
| 172 | + getList() { | |
| 173 | + this.listLoading = true | |
| 174 | + getInventoryList(this.query).then(response => { | |
| 175 | + if (response.code === 200) { | |
| 176 | + this.list = response.data.list || [] | |
| 177 | + this.total = (response.data.pagination && response.data.pagination.total) || 0 | |
| 178 | + } else { | |
| 179 | + this.$message.error(response.msg || '获取库存列表失败') | |
| 180 | + } | |
| 181 | + this.listLoading = false | |
| 182 | + }).catch(() => { | |
| 183 | + this.listLoading = false | |
| 184 | + }) | |
| 185 | + }, | |
| 186 | + | |
| 187 | + // 搜索 | |
| 188 | + search() { | |
| 189 | + this.query.currentPage = 1 | |
| 190 | + this.getList() | |
| 191 | + }, | |
| 192 | + | |
| 193 | + // 重置 | |
| 194 | + reset() { | |
| 195 | + this.query = { | |
| 196 | + productName: '', | |
| 197 | + productCategory: '', | |
| 198 | + departmentId: '', | |
| 199 | + currentPage: 1, | |
| 200 | + pageSize: 20 | |
| 201 | + } | |
| 202 | + this.getList() | |
| 203 | + }, | |
| 204 | + | |
| 205 | + // 添加库存 | |
| 206 | + addInventoryHandle() { | |
| 207 | + this.inventoryFormVisible = true | |
| 208 | + this.$nextTick(() => { | |
| 209 | + this.$refs.InventoryForm.init() | |
| 210 | + }) | |
| 211 | + }, | |
| 212 | + | |
| 213 | + // 编辑库存 | |
| 214 | + editInventoryHandle() { | |
| 215 | + if (this.selectedIds.length === 0) { | |
| 216 | + this.$message.warning('请选择一个库存记录') | |
| 217 | + return | |
| 218 | + } | |
| 219 | + if (this.selectedIds.length > 1) { | |
| 220 | + this.$message.warning('请只选择一个库存记录') | |
| 221 | + return | |
| 222 | + } | |
| 223 | + | |
| 224 | + const selectedInventory = this.list.find(item => item.id === this.selectedIds[0]) | |
| 225 | + if (selectedInventory) { | |
| 226 | + this.inventoryFormVisible = true | |
| 227 | + this.$nextTick(() => { | |
| 228 | + this.$refs.InventoryForm.init(selectedInventory.id) | |
| 229 | + }) | |
| 230 | + } | |
| 231 | + }, | |
| 232 | + | |
| 233 | + // 编辑库存(从操作列) | |
| 234 | + editInventory(row) { | |
| 235 | + this.inventoryFormVisible = true | |
| 236 | + this.$nextTick(() => { | |
| 237 | + this.$refs.InventoryForm.init(row.id) | |
| 238 | + }) | |
| 239 | + }, | |
| 240 | + | |
| 241 | + // 查看库存详情 | |
| 242 | + viewInventoryInfo(row) { | |
| 243 | + this.inventoryInfoVisible = true | |
| 244 | + this.$nextTick(() => { | |
| 245 | + this.$refs.InventoryInfoDialog.init(row.id) | |
| 246 | + }) | |
| 247 | + }, | |
| 248 | + | |
| 249 | + // 使用记录管理 | |
| 250 | + usageRecordHandle() { | |
| 251 | + this.usageRecordVisible = true | |
| 252 | + this.$nextTick(() => { | |
| 253 | + this.$refs.UsageRecordDialog.init() | |
| 254 | + }) | |
| 255 | + }, | |
| 256 | + | |
| 257 | + // 查看使用记录 | |
| 258 | + viewUsageRecords(row) { | |
| 259 | + this.usageRecordVisible = true | |
| 260 | + this.$nextTick(() => { | |
| 261 | + this.$refs.UsageRecordDialog.init(row.id, row.productName) | |
| 262 | + }) | |
| 263 | + }, | |
| 264 | + | |
| 265 | + // 多选 | |
| 266 | + handleSelectionChange(selection) { | |
| 267 | + this.selectedIds = selection.map(item => item.id) | |
| 268 | + }, | |
| 269 | + | |
| 270 | + // 格式化金额 | |
| 271 | + formatMoney(value) { | |
| 272 | + if (value === null || value === undefined || value === '') { | |
| 273 | + return '0.00' | |
| 274 | + } | |
| 275 | + return Number(value).toFixed(2) | |
| 276 | + }, | |
| 277 | + | |
| 278 | + // 导出 | |
| 279 | + exportData() { | |
| 280 | + this.$message.info('导出功能开发中...') | |
| 281 | + } | |
| 282 | + } | |
| 283 | +} | |
| 284 | +</script> | |
| 285 | + | |
| 286 | +<style scoped> | |
| 287 | +.NCC-common-layout { | |
| 288 | + height: 100%; | |
| 289 | +} | |
| 290 | + | |
| 291 | +.text-danger { | |
| 292 | + color: #f56c6c; | |
| 293 | + font-weight: bold; | |
| 294 | +} | |
| 295 | +</style> | ... | ... |
antis-ncc-admin/src/views/lqStudyClass/AddStudentForm.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog title="添加学员" :visible.sync="visible" width="800px" @close="closeDialog"> | |
| 3 | + <el-form ref="form" :model="form" :rules="rules" label-width="100px"> | |
| 4 | + <el-row :gutter="20"> | |
| 5 | + <el-col :span="12"> | |
| 6 | + <el-form-item label="班级名称"> | |
| 7 | + <el-input v-model="className" placeholder="班级名称" readonly /> | |
| 8 | + </el-form-item> | |
| 9 | + </el-col> | |
| 10 | + <el-col :span="12"> | |
| 11 | + <el-form-item label="班级ID"> | |
| 12 | + <el-input v-model="form.classId" placeholder="班级ID" readonly /> | |
| 13 | + </el-form-item> | |
| 14 | + </el-col> | |
| 15 | + </el-row> | |
| 16 | + | |
| 17 | + <!-- 学员信息 --> | |
| 18 | + <el-form-item label="学员信息"> | |
| 19 | + <div class="student-section"> | |
| 20 | + <div class="student-header"> | |
| 21 | + <span>学员列表</span> | |
| 22 | + <el-button type="primary" size="small" icon="el-icon-plus" @click="addStudent">添加学员</el-button> | |
| 23 | + </div> | |
| 24 | + <el-table :data="form.students" border style="width: 100%; margin-top: 10px;"> | |
| 25 | + <el-table-column prop="employeeId" label="选择学员" width="200"> | |
| 26 | + <template slot-scope="scope"> | |
| 27 | + <userSelect v-model="scope.row.employeeId" placeholder="请选择学员" | |
| 28 | + @change="(ids, users) => onStudentChange(scope.$index, ids, users)" size="small" /> | |
| 29 | + </template> | |
| 30 | + </el-table-column> | |
| 31 | + <el-table-column prop="employeeName" label="学员姓名" width="120"> | |
| 32 | + <template slot-scope="scope"> | |
| 33 | + <el-input v-model="scope.row.employeeName" placeholder="学员姓名" size="small" readonly /> | |
| 34 | + </template> | |
| 35 | + </el-table-column> | |
| 36 | + <el-table-column prop="employeePhone" label="手机号" width="140"> | |
| 37 | + <template slot-scope="scope"> | |
| 38 | + <el-input v-model="scope.row.employeePhone" placeholder="请输入手机号" size="small" /> | |
| 39 | + </template> | |
| 40 | + </el-table-column> | |
| 41 | + <el-table-column prop="admissionTime" label="入学时间" width="140"> | |
| 42 | + <template slot-scope="scope"> | |
| 43 | + <el-date-picker v-model="scope.row.admissionTime" type="date" placeholder="选择时间" | |
| 44 | + value-format="yyyy-MM-dd" size="small" style="width: 100%"> | |
| 45 | + </el-date-picker> | |
| 46 | + </template> | |
| 47 | + </el-table-column> | |
| 48 | + <el-table-column prop="hrBelong" label="HR归属" width="120"> | |
| 49 | + <template slot-scope="scope"> | |
| 50 | + <el-input v-model="scope.row.hrBelong" placeholder="请输入HR归属" size="small" /> | |
| 51 | + </template> | |
| 52 | + </el-table-column> | |
| 53 | + <el-table-column label="操作" width="80"> | |
| 54 | + <template slot-scope="scope"> | |
| 55 | + <el-button type="text" size="small" @click="removeStudent(scope.$index)">删除</el-button> | |
| 56 | + </template> | |
| 57 | + </el-table-column> | |
| 58 | + </el-table> | |
| 59 | + </div> | |
| 60 | + </el-form-item> | |
| 61 | + </el-form> | |
| 62 | + | |
| 63 | + <div slot="footer" class="dialog-footer"> | |
| 64 | + <el-button @click="closeDialog">取消</el-button> | |
| 65 | + <el-button type="primary" @click="submitForm" :loading="submitLoading">确定</el-button> | |
| 66 | + </div> | |
| 67 | + </el-dialog> | |
| 68 | +</template> | |
| 69 | + | |
| 70 | +<script> | |
| 71 | +import { addStudentsToClass } from '@/api/extend/lqStudyClass' | |
| 72 | + | |
| 73 | +export default { | |
| 74 | + name: 'AddStudentForm', | |
| 75 | + data() { | |
| 76 | + return { | |
| 77 | + visible: false, | |
| 78 | + submitLoading: false, | |
| 79 | + className: '', | |
| 80 | + form: { | |
| 81 | + classId: '', | |
| 82 | + students: [] | |
| 83 | + }, | |
| 84 | + rules: { | |
| 85 | + classId: [ | |
| 86 | + { required: true, message: '班级ID不能为空', trigger: 'blur' } | |
| 87 | + ] | |
| 88 | + } | |
| 89 | + } | |
| 90 | + }, | |
| 91 | + methods: { | |
| 92 | + init(classId = '', className = '') { | |
| 93 | + this.visible = true | |
| 94 | + this.className = className | |
| 95 | + this.form.classId = classId | |
| 96 | + this.form.students = [] | |
| 97 | + }, | |
| 98 | + | |
| 99 | + // 学员选择变化 | |
| 100 | + onStudentChange(index, ids, users) { | |
| 101 | + if (users && users.length > 0) { | |
| 102 | + const user = users[0] | |
| 103 | + this.form.students[index].employeeName = user.fullName | |
| 104 | + // 手机号需要用户手动填写,因为用户选择组件没有提供手机号信息 | |
| 105 | + } else { | |
| 106 | + this.form.students[index].employeeName = '' | |
| 107 | + } | |
| 108 | + }, | |
| 109 | + | |
| 110 | + // 删除学员 | |
| 111 | + removeStudent(index) { | |
| 112 | + this.form.students.splice(index, 1) | |
| 113 | + }, | |
| 114 | + | |
| 115 | + // 提交表单 | |
| 116 | + submitForm() { | |
| 117 | + this.$refs.form.validate((valid) => { | |
| 118 | + if (valid) { | |
| 119 | + if (this.form.students.length === 0) { | |
| 120 | + this.$message.warning('请至少添加一名学员') | |
| 121 | + return | |
| 122 | + } | |
| 123 | + | |
| 124 | + this.submitLoading = true | |
| 125 | + addStudentsToClass(this.form).then(response => { | |
| 126 | + if (response.code === 200) { | |
| 127 | + this.$message.success(response.message || '添加成功') | |
| 128 | + this.closeDialog() | |
| 129 | + this.$emit('refreshDataList') | |
| 130 | + } else { | |
| 131 | + this.$message.error(response.msg || '添加失败') | |
| 132 | + } | |
| 133 | + this.submitLoading = false | |
| 134 | + }).catch(() => { | |
| 135 | + this.submitLoading = false | |
| 136 | + }) | |
| 137 | + } | |
| 138 | + }) | |
| 139 | + }, | |
| 140 | + | |
| 141 | + // 关闭弹窗 | |
| 142 | + closeDialog() { | |
| 143 | + this.visible = false | |
| 144 | + this.className = '' | |
| 145 | + this.form = { | |
| 146 | + classId: '', | |
| 147 | + students: [] | |
| 148 | + } | |
| 149 | + } | |
| 150 | + } | |
| 151 | +} | |
| 152 | +</script> | |
| 153 | + | |
| 154 | +<style scoped> | |
| 155 | +.student-section { | |
| 156 | + width: 100%; | |
| 157 | +} | |
| 158 | + | |
| 159 | +.student-header { | |
| 160 | + display: flex; | |
| 161 | + justify-content: space-between; | |
| 162 | + align-items: center; | |
| 163 | + font-weight: 600; | |
| 164 | + color: #303133; | |
| 165 | +} | |
| 166 | +</style> | ... | ... |
antis-ncc-admin/src/views/lqStudyClass/CreateClassForm.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog title="创建学习班级" :visible.sync="visible" width="800px" @close="closeDialog"> | |
| 3 | + <el-form ref="form" :model="form" :rules="rules" label-width="100px"> | |
| 4 | + <el-row :gutter="20"> | |
| 5 | + <el-col :span="12"> | |
| 6 | + <el-form-item label="班级名称" prop="className"> | |
| 7 | + <el-input v-model="form.className" placeholder="请输入班级名称" /> | |
| 8 | + </el-form-item> | |
| 9 | + </el-col> | |
| 10 | + <el-col :span="12"> | |
| 11 | + <el-form-item label="授课老师" prop="teacherId"> | |
| 12 | + <userSelect v-model="form.teacherId" placeholder="请选择授课老师" @change="onTeacherChange" /> | |
| 13 | + </el-form-item> | |
| 14 | + </el-col> | |
| 15 | + </el-row> | |
| 16 | + | |
| 17 | + <el-row :gutter="20"> | |
| 18 | + <el-col :span="12"> | |
| 19 | + <el-form-item label="开始时间" prop="startTime"> | |
| 20 | + <el-date-picker v-model="form.startTime" type="datetime" placeholder="选择开始时间" | |
| 21 | + value-format="yyyy-MM-dd HH:mm:ss" style="width: 100%"> | |
| 22 | + </el-date-picker> | |
| 23 | + </el-form-item> | |
| 24 | + </el-col> | |
| 25 | + <el-col :span="12"> | |
| 26 | + <el-form-item label="结束时间" prop="endTime"> | |
| 27 | + <el-date-picker v-model="form.endTime" type="datetime" placeholder="选择结束时间" | |
| 28 | + value-format="yyyy-MM-dd HH:mm:ss" style="width: 100%"> | |
| 29 | + </el-date-picker> | |
| 30 | + </el-form-item> | |
| 31 | + </el-col> | |
| 32 | + </el-row> | |
| 33 | + | |
| 34 | + <el-form-item label="备注"> | |
| 35 | + <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注信息" /> | |
| 36 | + </el-form-item> | |
| 37 | + | |
| 38 | + <!-- 学员信息 --> | |
| 39 | + <el-form-item label="学员信息"> | |
| 40 | + <div class="student-section"> | |
| 41 | + <div class="student-header"> | |
| 42 | + <span>学员列表</span> | |
| 43 | + <el-button type="primary" size="small" icon="el-icon-plus" @click="addStudent">添加学员</el-button> | |
| 44 | + </div> | |
| 45 | + <el-table :data="form.students" border style="width: 100%; margin-top: 10px;"> | |
| 46 | + <el-table-column prop="employeeId" label="选择学员" width="200"> | |
| 47 | + <template slot-scope="scope"> | |
| 48 | + <userSelect v-model="scope.row.employeeId" placeholder="请选择学员" | |
| 49 | + @change="(ids, users) => onStudentChange(scope.$index, ids, users)" size="small" /> | |
| 50 | + </template> | |
| 51 | + </el-table-column> | |
| 52 | + <el-table-column prop="employeeName" label="学员姓名" width="120"> | |
| 53 | + <template slot-scope="scope"> | |
| 54 | + <el-input v-model="scope.row.employeeName" placeholder="学员姓名" size="small" readonly /> | |
| 55 | + </template> | |
| 56 | + </el-table-column> | |
| 57 | + <el-table-column prop="employeePhone" label="手机号" width="140"> | |
| 58 | + <template slot-scope="scope"> | |
| 59 | + <el-input v-model="scope.row.employeePhone" placeholder="请输入手机号" size="small" /> | |
| 60 | + </template> | |
| 61 | + </el-table-column> | |
| 62 | + <el-table-column prop="admissionTime" label="入学时间" width="140"> | |
| 63 | + <template slot-scope="scope"> | |
| 64 | + <el-date-picker v-model="scope.row.admissionTime" type="date" placeholder="选择时间" | |
| 65 | + value-format="yyyy-MM-dd" size="small" style="width: 100%"> | |
| 66 | + </el-date-picker> | |
| 67 | + </template> | |
| 68 | + </el-table-column> | |
| 69 | + <el-table-column prop="hrBelong" label="HR归属" width="120"> | |
| 70 | + <template slot-scope="scope"> | |
| 71 | + <el-input v-model="scope.row.hrBelong" placeholder="请输入HR归属" size="small" /> | |
| 72 | + </template> | |
| 73 | + </el-table-column> | |
| 74 | + <el-table-column label="操作" width="80"> | |
| 75 | + <template slot-scope="scope"> | |
| 76 | + <el-button type="text" size="small" @click="removeStudent(scope.$index)">删除</el-button> | |
| 77 | + </template> | |
| 78 | + </el-table-column> | |
| 79 | + </el-table> | |
| 80 | + </div> | |
| 81 | + </el-form-item> | |
| 82 | + </el-form> | |
| 83 | + | |
| 84 | + <div slot="footer" class="dialog-footer"> | |
| 85 | + <el-button @click="closeDialog">取消</el-button> | |
| 86 | + <el-button type="primary" @click="submitForm" :loading="submitLoading">确定</el-button> | |
| 87 | + </div> | |
| 88 | + </el-dialog> | |
| 89 | +</template> | |
| 90 | + | |
| 91 | +<script> | |
| 92 | +import { createClassWithStudents } from '@/api/extend/lqStudyClass' | |
| 93 | + | |
| 94 | +export default { | |
| 95 | + name: 'CreateClassForm', | |
| 96 | + data() { | |
| 97 | + return { | |
| 98 | + visible: false, | |
| 99 | + submitLoading: false, | |
| 100 | + form: { | |
| 101 | + className: '', | |
| 102 | + teacherId: '', | |
| 103 | + teacherName: '', | |
| 104 | + startTime: '', | |
| 105 | + endTime: '', | |
| 106 | + remark: '', | |
| 107 | + students: [] | |
| 108 | + }, | |
| 109 | + rules: { | |
| 110 | + className: [ | |
| 111 | + { required: true, message: '请输入班级名称', trigger: 'blur' } | |
| 112 | + ], | |
| 113 | + teacherId: [ | |
| 114 | + { required: true, message: '请选择授课老师', trigger: 'change' } | |
| 115 | + ], | |
| 116 | + startTime: [ | |
| 117 | + { required: true, message: '请选择开始时间', trigger: 'change' } | |
| 118 | + ], | |
| 119 | + endTime: [ | |
| 120 | + { required: true, message: '请选择结束时间', trigger: 'change' } | |
| 121 | + ] | |
| 122 | + } | |
| 123 | + } | |
| 124 | + }, | |
| 125 | + methods: { | |
| 126 | + init() { | |
| 127 | + this.visible = true | |
| 128 | + this.resetForm() | |
| 129 | + }, | |
| 130 | + | |
| 131 | + resetForm() { | |
| 132 | + this.form = { | |
| 133 | + className: '', | |
| 134 | + teacherId: '', | |
| 135 | + teacherName: '', | |
| 136 | + startTime: '', | |
| 137 | + endTime: '', | |
| 138 | + remark: '', | |
| 139 | + students: [] | |
| 140 | + } | |
| 141 | + this.$nextTick(() => { | |
| 142 | + if (this.$refs.form) { | |
| 143 | + this.$refs.form.clearValidate() | |
| 144 | + } | |
| 145 | + }) | |
| 146 | + }, | |
| 147 | + | |
| 148 | + // 老师选择变化 | |
| 149 | + onTeacherChange(ids, users) { | |
| 150 | + if (users && users.length > 0) { | |
| 151 | + this.form.teacherName = users[0].fullName | |
| 152 | + } else { | |
| 153 | + this.form.teacherName = '' | |
| 154 | + } | |
| 155 | + }, | |
| 156 | + | |
| 157 | + // 学员选择变化 | |
| 158 | + onStudentChange(index, ids, users) { | |
| 159 | + if (users && users.length > 0) { | |
| 160 | + const user = users[0] | |
| 161 | + this.form.students[index].employeeName = user.fullName | |
| 162 | + // 手机号需要用户手动填写,因为用户选择组件没有提供手机号信息 | |
| 163 | + } else { | |
| 164 | + this.form.students[index].employeeName = '' | |
| 165 | + } | |
| 166 | + }, | |
| 167 | + | |
| 168 | + // 添加学员 | |
| 169 | + addStudent() { | |
| 170 | + this.form.students.push({ | |
| 171 | + employeeName: '', | |
| 172 | + employeePhone: '', | |
| 173 | + employeeId: '', | |
| 174 | + admissionTime: '', | |
| 175 | + hrBelong: '' | |
| 176 | + }) | |
| 177 | + }, | |
| 178 | + | |
| 179 | + // 提交表单 | |
| 180 | + submitForm() { | |
| 181 | + this.$refs.form.validate((valid) => { | |
| 182 | + if (valid) { | |
| 183 | + if (this.form.students.length === 0) { | |
| 184 | + this.$message.warning('请至少添加一名学员') | |
| 185 | + return | |
| 186 | + } | |
| 187 | + | |
| 188 | + this.submitLoading = true | |
| 189 | + createClassWithStudents(this.form).then(response => { | |
| 190 | + if (response.code === 200) { | |
| 191 | + this.$message.success(response.message || '创建成功') | |
| 192 | + this.closeDialog() | |
| 193 | + this.$emit('refreshDataList') | |
| 194 | + } else { | |
| 195 | + this.$message.error(response.msg || '创建失败') | |
| 196 | + } | |
| 197 | + this.submitLoading = false | |
| 198 | + }).catch(() => { | |
| 199 | + this.submitLoading = false | |
| 200 | + }) | |
| 201 | + } | |
| 202 | + }) | |
| 203 | + }, | |
| 204 | + | |
| 205 | + // 关闭弹窗 | |
| 206 | + closeDialog() { | |
| 207 | + this.visible = false | |
| 208 | + this.resetForm() | |
| 209 | + } | |
| 210 | + } | |
| 211 | +} | |
| 212 | +</script> | |
| 213 | + | |
| 214 | +<style scoped> | |
| 215 | +.student-section { | |
| 216 | + width: 100%; | |
| 217 | +} | |
| 218 | + | |
| 219 | +.student-header { | |
| 220 | + display: flex; | |
| 221 | + justify-content: space-between; | |
| 222 | + align-items: center; | |
| 223 | + font-weight: 600; | |
| 224 | + color: #303133; | |
| 225 | +} | |
| 226 | +</style> | ... | ... |
antis-ncc-admin/src/views/lqStudyClass/StudentListDialog.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog title="班级学员列表" :visible.sync="visible" width="1000px" @close="closeDialog"> | |
| 3 | + <div class="student-list-container"> | |
| 4 | + <!-- 搜索区域 --> | |
| 5 | + <div class="search-section"> | |
| 6 | + <el-form :model="query" :inline="true" class="search-form"> | |
| 7 | + <el-form-item label="学员姓名"> | |
| 8 | + <el-input v-model="query.employeeName" placeholder="请输入学员姓名" clearable size="small" /> | |
| 9 | + </el-form-item> | |
| 10 | + <el-form-item label="手机号"> | |
| 11 | + <el-input v-model="query.employeePhone" placeholder="请输入手机号" clearable size="small" /> | |
| 12 | + </el-form-item> | |
| 13 | + <el-form-item label="员工ID"> | |
| 14 | + <el-input v-model="query.employeeId" placeholder="请输入员工ID" clearable size="small" /> | |
| 15 | + </el-form-item> | |
| 16 | + <el-form-item label="HR归属"> | |
| 17 | + <el-input v-model="query.hrBelong" placeholder="请输入HR归属" clearable size="small" /> | |
| 18 | + </el-form-item> | |
| 19 | + <el-form-item> | |
| 20 | + <el-button type="primary" size="small" @click="search">搜索</el-button> | |
| 21 | + <el-button size="small" @click="reset">重置</el-button> | |
| 22 | + </el-form-item> | |
| 23 | + </el-form> | |
| 24 | + </div> | |
| 25 | + | |
| 26 | + <!-- 学员列表 --> | |
| 27 | + <div class="table-section"> | |
| 28 | + <el-table :data="studentList" v-loading="loading" border stripe style="width: 100%"> | |
| 29 | + <el-table-column prop="employeeName" label="学员姓名" width="120" fixed="left" /> | |
| 30 | + <el-table-column prop="employeePhone" label="手机号" width="140" /> | |
| 31 | + <el-table-column prop="employeeId" label="员工ID" width="120" /> | |
| 32 | + <el-table-column prop="admissionTime" label="入学时间" width="120"> | |
| 33 | + <template slot-scope="scope"> | |
| 34 | + {{ scope.row.admissionTime | dateFormat }} | |
| 35 | + </template> | |
| 36 | + </el-table-column> | |
| 37 | + <el-table-column prop="className" label="班级名称" width="150" /> | |
| 38 | + <el-table-column prop="hrBelong" label="HR归属" width="120" /> | |
| 39 | + <el-table-column label="操作" width="120" fixed="right"> | |
| 40 | + <template slot-scope="scope"> | |
| 41 | + <el-button type="text" size="small" @click="viewStudyRecords(scope.row)">学习记录</el-button> | |
| 42 | + </template> | |
| 43 | + </el-table-column> | |
| 44 | + </el-table> | |
| 45 | + | |
| 46 | + <!-- 分页 --> | |
| 47 | + <div class="pagination-section"> | |
| 48 | + <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" | |
| 49 | + :current-page="pagination.pageIndex" :page-sizes="[10, 20, 50, 100]" | |
| 50 | + :page-size="pagination.pageSize" layout="total, sizes, prev, pager, next, jumper" | |
| 51 | + :total="pagination.total"> | |
| 52 | + </el-pagination> | |
| 53 | + </div> | |
| 54 | + </div> | |
| 55 | + </div> | |
| 56 | + | |
| 57 | + <div slot="footer" class="dialog-footer"> | |
| 58 | + <el-button @click="closeDialog">关闭</el-button> | |
| 59 | + </div> | |
| 60 | + </el-dialog> | |
| 61 | +</template> | |
| 62 | + | |
| 63 | +<script> | |
| 64 | +import { getStudentListByClassId } from '@/api/extend/lqStudyClass' | |
| 65 | + | |
| 66 | +export default { | |
| 67 | + name: 'StudentListDialog', | |
| 68 | + data() { | |
| 69 | + return { | |
| 70 | + visible: false, | |
| 71 | + loading: false, | |
| 72 | + classId: '', | |
| 73 | + className: '', | |
| 74 | + studentList: [], | |
| 75 | + query: { | |
| 76 | + employeeName: '', | |
| 77 | + employeePhone: '', | |
| 78 | + employeeId: '', | |
| 79 | + hrBelong: '', | |
| 80 | + classId: '' | |
| 81 | + }, | |
| 82 | + pagination: { | |
| 83 | + pageIndex: 1, | |
| 84 | + pageSize: 20, | |
| 85 | + total: 0 | |
| 86 | + } | |
| 87 | + } | |
| 88 | + }, | |
| 89 | + methods: { | |
| 90 | + init(classId, className) { | |
| 91 | + this.visible = true | |
| 92 | + this.classId = classId | |
| 93 | + this.className = className | |
| 94 | + this.query.classId = classId | |
| 95 | + this.getStudentList() | |
| 96 | + }, | |
| 97 | + | |
| 98 | + // 获取学员列表 | |
| 99 | + getStudentList() { | |
| 100 | + this.loading = true | |
| 101 | + const params = { | |
| 102 | + ...this.query, | |
| 103 | + currentPage: this.pagination.pageIndex, | |
| 104 | + pageSize: this.pagination.pageSize | |
| 105 | + } | |
| 106 | + | |
| 107 | + getStudentListByClassId(params).then(response => { | |
| 108 | + if (response.code === 200) { | |
| 109 | + this.studentList = response.data.list || [] | |
| 110 | + this.pagination.total = (response.data.pagination && response.data.pagination.total) || 0 | |
| 111 | + } else { | |
| 112 | + this.$message.error(response.msg || '获取学员列表失败') | |
| 113 | + } | |
| 114 | + this.loading = false | |
| 115 | + }).catch(() => { | |
| 116 | + this.loading = false | |
| 117 | + }) | |
| 118 | + }, | |
| 119 | + | |
| 120 | + // 搜索 | |
| 121 | + search() { | |
| 122 | + this.pagination.pageIndex = 1 | |
| 123 | + this.getStudentList() | |
| 124 | + }, | |
| 125 | + | |
| 126 | + // 重置 | |
| 127 | + reset() { | |
| 128 | + this.query = { | |
| 129 | + employeeName: '', | |
| 130 | + employeePhone: '', | |
| 131 | + employeeId: '', | |
| 132 | + hrBelong: '', | |
| 133 | + classId: this.classId | |
| 134 | + } | |
| 135 | + this.pagination.pageIndex = 1 | |
| 136 | + this.getStudentList() | |
| 137 | + }, | |
| 138 | + | |
| 139 | + // 分页大小改变 | |
| 140 | + handleSizeChange(val) { | |
| 141 | + this.pagination.pageSize = val | |
| 142 | + this.pagination.pageIndex = 1 | |
| 143 | + this.getStudentList() | |
| 144 | + }, | |
| 145 | + | |
| 146 | + // 当前页改变 | |
| 147 | + handleCurrentChange(val) { | |
| 148 | + this.pagination.pageIndex = val | |
| 149 | + this.getStudentList() | |
| 150 | + }, | |
| 151 | + | |
| 152 | + // 查看学习记录 | |
| 153 | + viewStudyRecords(row) { | |
| 154 | + this.$emit('viewStudyRecords', row) | |
| 155 | + }, | |
| 156 | + | |
| 157 | + // 关闭弹窗 | |
| 158 | + closeDialog() { | |
| 159 | + this.visible = false | |
| 160 | + this.studentList = [] | |
| 161 | + this.query = { | |
| 162 | + employeeName: '', | |
| 163 | + employeePhone: '', | |
| 164 | + employeeId: '', | |
| 165 | + hrBelong: '', | |
| 166 | + classId: '' | |
| 167 | + } | |
| 168 | + this.pagination = { | |
| 169 | + pageIndex: 1, | |
| 170 | + pageSize: 20, | |
| 171 | + total: 0 | |
| 172 | + } | |
| 173 | + } | |
| 174 | + } | |
| 175 | +} | |
| 176 | +</script> | |
| 177 | + | |
| 178 | +<style scoped> | |
| 179 | +.student-list-container { | |
| 180 | + padding: 20px; | |
| 181 | +} | |
| 182 | + | |
| 183 | +.search-section { | |
| 184 | + margin-bottom: 20px; | |
| 185 | + padding: 15px; | |
| 186 | + background: #f5f7fa; | |
| 187 | + border-radius: 4px; | |
| 188 | +} | |
| 189 | + | |
| 190 | +.search-form .el-form-item { | |
| 191 | + margin-bottom: 10px; | |
| 192 | +} | |
| 193 | + | |
| 194 | +.table-section { | |
| 195 | + margin-bottom: 20px; | |
| 196 | +} | |
| 197 | + | |
| 198 | +.pagination-section { | |
| 199 | + text-align: right; | |
| 200 | + margin-top: 20px; | |
| 201 | +} | |
| 202 | +</style> | ... | ... |
antis-ncc-admin/src/views/lqStudyClass/StudyRecordDialog.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog title="学习记录管理" :visible.sync="visible" width="1200px" @close="closeDialog"> | |
| 3 | + <div class="study-record-container"> | |
| 4 | + <!-- 搜索区域 --> | |
| 5 | + <div class="search-section"> | |
| 6 | + <el-form :model="query" :inline="true" class="search-form"> | |
| 7 | + <el-form-item label="学员姓名"> | |
| 8 | + <el-input v-model="query.employeeName" placeholder="请输入学员姓名" clearable size="small" /> | |
| 9 | + </el-form-item> | |
| 10 | + <el-form-item label="员工ID"> | |
| 11 | + <el-input v-model="query.employeeId" placeholder="请输入员工ID" clearable size="small" /> | |
| 12 | + </el-form-item> | |
| 13 | + <el-form-item label="学习类型"> | |
| 14 | + <el-input v-model="query.studyType" placeholder="请输入学习类型" clearable size="small" /> | |
| 15 | + </el-form-item> | |
| 16 | + <el-form-item label="学习日期"> | |
| 17 | + <el-date-picker v-model="query.studyDateRange" type="daterange" range-separator="至" | |
| 18 | + start-placeholder="开始日期" end-placeholder="结束日期" value-format="yyyy-MM-dd" size="small"> | |
| 19 | + </el-date-picker> | |
| 20 | + </el-form-item> | |
| 21 | + <el-form-item label="日常状态"> | |
| 22 | + <el-input v-model="query.dailyStatus" placeholder="请输入日常状态" clearable size="small" /> | |
| 23 | + </el-form-item> | |
| 24 | + <el-form-item> | |
| 25 | + <el-button type="primary" size="small" @click="search">搜索</el-button> | |
| 26 | + <el-button size="small" @click="reset">重置</el-button> | |
| 27 | + <el-button type="success" size="small" icon="el-icon-plus" @click="addRecord">添加记录</el-button> | |
| 28 | + </el-form-item> | |
| 29 | + </el-form> | |
| 30 | + </div> | |
| 31 | + | |
| 32 | + <!-- 学习记录列表 --> | |
| 33 | + <div class="table-section"> | |
| 34 | + <el-table :data="recordList" v-loading="loading" border stripe style="width: 100%"> | |
| 35 | + <el-table-column prop="employeeName" label="学员姓名" width="100" fixed="left" /> | |
| 36 | + <el-table-column prop="employeeId" label="员工ID" width="120" /> | |
| 37 | + <el-table-column prop="studyType" label="学习类型" width="120" /> | |
| 38 | + <el-table-column prop="transportFee" label="交通费" width="100" align="center"> | |
| 39 | + <template slot-scope="scope"> | |
| 40 | + {{ formatMoney(scope.row.transportFee) }} | |
| 41 | + </template> | |
| 42 | + </el-table-column> | |
| 43 | + <el-table-column prop="studyDate" label="学习日期" width="120"> | |
| 44 | + <template slot-scope="scope"> | |
| 45 | + {{ scope.row.studyDate | dateFormat }} | |
| 46 | + </template> | |
| 47 | + </el-table-column> | |
| 48 | + <el-table-column prop="dailyStatus" label="日常状态" width="120" /> | |
| 49 | + <el-table-column prop="remark" label="备注" show-overflow-tooltip /> | |
| 50 | + <el-table-column prop="isStoreAssist" label="下店协助" width="100" align="center"> | |
| 51 | + <template slot-scope="scope"> | |
| 52 | + <el-tag :type="scope.row.isStoreAssist === 1 ? 'success' : 'info'" size="mini"> | |
| 53 | + {{ scope.row.isStoreAssist === 1 ? '是' : '否' }} | |
| 54 | + </el-tag> | |
| 55 | + </template> | |
| 56 | + </el-table-column> | |
| 57 | + <el-table-column prop="storeName" label="协助门店" width="120" /> | |
| 58 | + <el-table-column prop="createUserName" label="创建人" width="100" /> | |
| 59 | + <el-table-column prop="createTime" label="创建时间" width="160"> | |
| 60 | + <template slot-scope="scope"> | |
| 61 | + {{ scope.row.createTime | dateTimeFormat }} | |
| 62 | + </template> | |
| 63 | + </el-table-column> | |
| 64 | + <el-table-column label="操作" width="120" fixed="right"> | |
| 65 | + <template slot-scope="scope"> | |
| 66 | + <el-button type="text" size="small" @click="cancelRecord(scope.row)">作废</el-button> | |
| 67 | + </template> | |
| 68 | + </el-table-column> | |
| 69 | + </el-table> | |
| 70 | + | |
| 71 | + <!-- 分页 --> | |
| 72 | + <div class="pagination-section"> | |
| 73 | + <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" | |
| 74 | + :current-page="pagination.pageIndex" :page-sizes="[10, 20, 50, 100]" | |
| 75 | + :page-size="pagination.pageSize" layout="total, sizes, prev, pager, next, jumper" | |
| 76 | + :total="pagination.total"> | |
| 77 | + </el-pagination> | |
| 78 | + </div> | |
| 79 | + </div> | |
| 80 | + </div> | |
| 81 | + | |
| 82 | + <div slot="footer" class="dialog-footer"> | |
| 83 | + <el-button @click="closeDialog">关闭</el-button> | |
| 84 | + </div> | |
| 85 | + | |
| 86 | + <!-- 添加学习记录弹窗 --> | |
| 87 | + <el-dialog title="添加学习记录" :visible.sync="addRecordVisible" width="600px" append-to-body> | |
| 88 | + <el-form ref="recordForm" :model="recordForm" :rules="recordRules" label-width="100px"> | |
| 89 | + <el-form-item label="学员姓名" prop="employeeName"> | |
| 90 | + <el-input v-model="recordForm.employeeName" placeholder="请输入学员姓名" /> | |
| 91 | + </el-form-item> | |
| 92 | + <el-form-item label="员工ID" prop="employeeId"> | |
| 93 | + <el-input v-model="recordForm.employeeId" placeholder="请输入员工ID" /> | |
| 94 | + </el-form-item> | |
| 95 | + <el-form-item label="学习类型" prop="studyType"> | |
| 96 | + <el-input v-model="recordForm.studyType" placeholder="请输入学习类型" /> | |
| 97 | + </el-form-item> | |
| 98 | + <el-form-item label="交通费"> | |
| 99 | + <el-input-number v-model="recordForm.transportFee" :min="0" :precision="2" style="width: 100%" /> | |
| 100 | + </el-form-item> | |
| 101 | + <el-form-item label="学习日期" prop="studyDate"> | |
| 102 | + <el-date-picker v-model="recordForm.studyDate" type="date" placeholder="选择学习日期" | |
| 103 | + value-format="yyyy-MM-dd" style="width: 100%"> | |
| 104 | + </el-date-picker> | |
| 105 | + </el-form-item> | |
| 106 | + <el-form-item label="日常状态" prop="dailyStatus"> | |
| 107 | + <el-input v-model="recordForm.dailyStatus" placeholder="请输入日常状态" /> | |
| 108 | + </el-form-item> | |
| 109 | + <el-form-item label="是否下店协助"> | |
| 110 | + <el-switch v-model="recordForm.isStoreAssist" :active-value="1" :inactive-value="0" /> | |
| 111 | + </el-form-item> | |
| 112 | + <el-form-item v-if="recordForm.isStoreAssist === 1" label="门店ID" prop="storeId"> | |
| 113 | + <el-input v-model="recordForm.storeId" placeholder="请输入门店ID" /> | |
| 114 | + </el-form-item> | |
| 115 | + <el-form-item v-if="recordForm.isStoreAssist === 1" label="门店名称" prop="storeName"> | |
| 116 | + <el-input v-model="recordForm.storeName" placeholder="请输入门店名称" /> | |
| 117 | + </el-form-item> | |
| 118 | + <el-form-item label="备注"> | |
| 119 | + <el-input v-model="recordForm.remark" type="textarea" :rows="3" placeholder="请输入备注信息" /> | |
| 120 | + </el-form-item> | |
| 121 | + </el-form> | |
| 122 | + <div slot="footer" class="dialog-footer"> | |
| 123 | + <el-button @click="addRecordVisible = false">取消</el-button> | |
| 124 | + <el-button type="primary" @click="submitRecord" :loading="submitLoading">确定</el-button> | |
| 125 | + </div> | |
| 126 | + </el-dialog> | |
| 127 | + </el-dialog> | |
| 128 | +</template> | |
| 129 | + | |
| 130 | +<script> | |
| 131 | +import { getStudyRecordList, addStudyRecord, cancelStudyRecord, getStudentListByClassId } from '@/api/extend/lqStudyClass' | |
| 132 | + | |
| 133 | +export default { | |
| 134 | + name: 'StudyRecordDialog', | |
| 135 | + data() { | |
| 136 | + return { | |
| 137 | + visible: false, | |
| 138 | + loading: false, | |
| 139 | + submitLoading: false, | |
| 140 | + addRecordVisible: false, | |
| 141 | + classId: '', | |
| 142 | + className: '', | |
| 143 | + studentIds: [], // 班级学员ID列表 | |
| 144 | + recordList: [], | |
| 145 | + query: { | |
| 146 | + employeeName: '', | |
| 147 | + employeeId: '', | |
| 148 | + studyType: '', | |
| 149 | + studyDateStart: '', | |
| 150 | + studyDateEnd: '', | |
| 151 | + dailyStatus: '', | |
| 152 | + studyDateRange: [] | |
| 153 | + }, | |
| 154 | + pagination: { | |
| 155 | + pageIndex: 1, | |
| 156 | + pageSize: 20, | |
| 157 | + total: 0 | |
| 158 | + }, | |
| 159 | + recordForm: { | |
| 160 | + employeeName: '', | |
| 161 | + employeeId: '', | |
| 162 | + studyType: '', | |
| 163 | + transportFee: 0, | |
| 164 | + studyDate: '', | |
| 165 | + dailyStatus: '', | |
| 166 | + remark: '', | |
| 167 | + isStoreAssist: 0, | |
| 168 | + storeId: '', | |
| 169 | + storeName: '' | |
| 170 | + }, | |
| 171 | + recordRules: { | |
| 172 | + employeeName: [ | |
| 173 | + { required: true, message: '请输入学员姓名', trigger: 'blur' } | |
| 174 | + ], | |
| 175 | + employeeId: [ | |
| 176 | + { required: true, message: '请输入员工ID', trigger: 'blur' } | |
| 177 | + ], | |
| 178 | + studyType: [ | |
| 179 | + { required: true, message: '请输入学习类型', trigger: 'blur' } | |
| 180 | + ], | |
| 181 | + studyDate: [ | |
| 182 | + { required: true, message: '请选择学习日期', trigger: 'change' } | |
| 183 | + ], | |
| 184 | + dailyStatus: [ | |
| 185 | + { required: true, message: '请输入日常状态', trigger: 'blur' } | |
| 186 | + ] | |
| 187 | + } | |
| 188 | + } | |
| 189 | + }, | |
| 190 | + methods: { | |
| 191 | + init(classId, className) { | |
| 192 | + this.visible = true | |
| 193 | + this.classId = classId | |
| 194 | + this.className = className | |
| 195 | + this.getStudentIds(classId) | |
| 196 | + }, | |
| 197 | + | |
| 198 | + // 获取班级下的学员ID列表 | |
| 199 | + getStudentIds(classId) { | |
| 200 | + const params = { | |
| 201 | + classId: classId, | |
| 202 | + currentPage: 1, | |
| 203 | + pageSize: 1000 // 获取所有学员 | |
| 204 | + } | |
| 205 | + | |
| 206 | + getStudentListByClassId(params).then(response => { | |
| 207 | + if (response.code === 200) { | |
| 208 | + const students = response.data.list || [] | |
| 209 | + this.studentIds = students.map(student => student.employeeId).filter(id => id) | |
| 210 | + this.getRecordList() | |
| 211 | + } else { | |
| 212 | + this.$message.error(response.msg || '获取学员列表失败') | |
| 213 | + this.loading = false | |
| 214 | + } | |
| 215 | + }).catch(() => { | |
| 216 | + this.loading = false | |
| 217 | + }) | |
| 218 | + }, | |
| 219 | + // 获取学习记录列表 | |
| 220 | + getRecordList() { | |
| 221 | + this.loading = true | |
| 222 | + const params = { | |
| 223 | + ...this.query, | |
| 224 | + currentPage: this.pagination.pageIndex, | |
| 225 | + pageSize: this.pagination.pageSize | |
| 226 | + } | |
| 227 | + | |
| 228 | + // 处理日期范围 | |
| 229 | + if (this.query.studyDateRange && this.query.studyDateRange.length === 2) { | |
| 230 | + params.studyDateStart = this.query.studyDateRange[0] | |
| 231 | + params.studyDateEnd = this.query.studyDateRange[1] | |
| 232 | + } | |
| 233 | + | |
| 234 | + getStudyRecordList(params).then(response => { | |
| 235 | + if (response.code === 200) { | |
| 236 | + let recordList = response.data.list || [] | |
| 237 | + // 按班级学员ID过滤学习记录 | |
| 238 | + if (this.studentIds && this.studentIds.length > 0) { | |
| 239 | + recordList = recordList.filter(record => this.studentIds.includes(record.employeeId)) | |
| 240 | + } | |
| 241 | + this.recordList = recordList | |
| 242 | + this.pagination.total = recordList.length | |
| 243 | + } else { | |
| 244 | + this.$message.error(response.msg || '获取学习记录失败') | |
| 245 | + } | |
| 246 | + this.loading = false | |
| 247 | + }).catch(() => { | |
| 248 | + this.loading = false | |
| 249 | + }) | |
| 250 | + }, | |
| 251 | + | |
| 252 | + // 搜索 | |
| 253 | + search() { | |
| 254 | + this.pagination.pageIndex = 1 | |
| 255 | + this.getRecordList() | |
| 256 | + }, | |
| 257 | + | |
| 258 | + // 重置 | |
| 259 | + reset() { | |
| 260 | + this.query = { | |
| 261 | + employeeName: '', | |
| 262 | + employeeId: '', | |
| 263 | + studyType: '', | |
| 264 | + studyDateStart: '', | |
| 265 | + studyDateEnd: '', | |
| 266 | + dailyStatus: '', | |
| 267 | + studyDateRange: [] | |
| 268 | + } | |
| 269 | + this.pagination.pageIndex = 1 | |
| 270 | + this.getRecordList() | |
| 271 | + }, | |
| 272 | + | |
| 273 | + // 分页大小改变 | |
| 274 | + handleSizeChange(val) { | |
| 275 | + this.pagination.pageSize = val | |
| 276 | + this.pagination.pageIndex = 1 | |
| 277 | + this.getRecordList() | |
| 278 | + }, | |
| 279 | + | |
| 280 | + // 当前页改变 | |
| 281 | + handleCurrentChange(val) { | |
| 282 | + this.pagination.pageIndex = val | |
| 283 | + this.getRecordList() | |
| 284 | + }, | |
| 285 | + | |
| 286 | + // 添加记录 | |
| 287 | + addRecord() { | |
| 288 | + this.addRecordVisible = true | |
| 289 | + this.resetRecordForm() | |
| 290 | + }, | |
| 291 | + | |
| 292 | + // 重置记录表单 | |
| 293 | + resetRecordForm() { | |
| 294 | + this.recordForm = { | |
| 295 | + employeeName: '', | |
| 296 | + employeeId: '', | |
| 297 | + studyType: '', | |
| 298 | + transportFee: 0, | |
| 299 | + studyDate: '', | |
| 300 | + dailyStatus: '', | |
| 301 | + remark: '', | |
| 302 | + isStoreAssist: 0, | |
| 303 | + storeId: '', | |
| 304 | + storeName: '' | |
| 305 | + } | |
| 306 | + this.$nextTick(() => { | |
| 307 | + if (this.$refs.recordForm) { | |
| 308 | + this.$refs.recordForm.clearValidate() | |
| 309 | + } | |
| 310 | + }) | |
| 311 | + }, | |
| 312 | + | |
| 313 | + // 提交记录 | |
| 314 | + submitRecord() { | |
| 315 | + this.$refs.recordForm.validate((valid) => { | |
| 316 | + if (valid) { | |
| 317 | + this.submitLoading = true | |
| 318 | + addStudyRecord(this.recordForm).then(response => { | |
| 319 | + if (response.code === 200) { | |
| 320 | + this.$message.success('添加成功') | |
| 321 | + this.addRecordVisible = false | |
| 322 | + this.getRecordList() | |
| 323 | + } else { | |
| 324 | + this.$message.error(response.msg || '添加失败') | |
| 325 | + } | |
| 326 | + this.submitLoading = false | |
| 327 | + }).catch(() => { | |
| 328 | + this.submitLoading = false | |
| 329 | + }) | |
| 330 | + } | |
| 331 | + }) | |
| 332 | + }, | |
| 333 | + | |
| 334 | + // 作废记录 | |
| 335 | + cancelRecord(row) { | |
| 336 | + this.$confirm('确定要作废该学习记录吗?', '提示', { | |
| 337 | + confirmButtonText: '确定', | |
| 338 | + cancelButtonText: '取消', | |
| 339 | + type: 'warning' | |
| 340 | + }).then(() => { | |
| 341 | + cancelStudyRecord(row.id).then(response => { | |
| 342 | + if (response.code === 200) { | |
| 343 | + this.$message.success('作废成功') | |
| 344 | + this.getRecordList() | |
| 345 | + } else { | |
| 346 | + this.$message.error(response.msg || '作废失败') | |
| 347 | + } | |
| 348 | + }) | |
| 349 | + }) | |
| 350 | + }, | |
| 351 | + | |
| 352 | + // 格式化金额 | |
| 353 | + formatMoney(value) { | |
| 354 | + if (value === null || value === undefined || value === '') { | |
| 355 | + return '0.00' | |
| 356 | + } | |
| 357 | + return Number(value).toFixed(2) | |
| 358 | + }, | |
| 359 | + | |
| 360 | + // 关闭弹窗 | |
| 361 | + closeDialog() { | |
| 362 | + this.visible = false | |
| 363 | + this.recordList = [] | |
| 364 | + this.query = { | |
| 365 | + employeeName: '', | |
| 366 | + employeeId: '', | |
| 367 | + studyType: '', | |
| 368 | + studyDateStart: '', | |
| 369 | + studyDateEnd: '', | |
| 370 | + dailyStatus: '', | |
| 371 | + studyDateRange: [] | |
| 372 | + } | |
| 373 | + this.pagination = { | |
| 374 | + pageIndex: 1, | |
| 375 | + pageSize: 20, | |
| 376 | + total: 0 | |
| 377 | + } | |
| 378 | + } | |
| 379 | + } | |
| 380 | +} | |
| 381 | +</script> | |
| 382 | + | |
| 383 | +<style scoped> | |
| 384 | +.study-record-container { | |
| 385 | + padding: 20px; | |
| 386 | +} | |
| 387 | + | |
| 388 | +.search-section { | |
| 389 | + margin-bottom: 20px; | |
| 390 | + padding: 15px; | |
| 391 | + background: #f5f7fa; | |
| 392 | + border-radius: 4px; | |
| 393 | +} | |
| 394 | + | |
| 395 | +.search-form .el-form-item { | |
| 396 | + margin-bottom: 10px; | |
| 397 | +} | |
| 398 | + | |
| 399 | +.table-section { | |
| 400 | + margin-bottom: 20px; | |
| 401 | +} | |
| 402 | + | |
| 403 | +.pagination-section { | |
| 404 | + text-align: right; | |
| 405 | + margin-top: 20px; | |
| 406 | +} | |
| 407 | +</style> | ... | ... |
antis-ncc-admin/src/views/lqStudyClass/index.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="班级名称"> | |
| 9 | + <el-input v-model="query.className" placeholder="请输入班级名称" clearable /> | |
| 10 | + </el-form-item> | |
| 11 | + </el-col> | |
| 12 | + <el-col :span="6"> | |
| 13 | + <el-form-item label="授课老师"> | |
| 14 | + <userSelect v-model="query.teacherId" placeholder="请选择授课老师" clearable /> | |
| 15 | + </el-form-item> | |
| 16 | + </el-col> | |
| 17 | + <el-col :span="6"> | |
| 18 | + <el-form-item label="开始时间"> | |
| 19 | + <el-date-picker v-model="query.startTime" type="date" placeholder="选择开始时间" | |
| 20 | + value-format="yyyy-MM-dd" clearable> | |
| 21 | + </el-date-picker> | |
| 22 | + </el-form-item> | |
| 23 | + </el-col> | |
| 24 | + <el-col :span="6"> | |
| 25 | + <el-form-item> | |
| 26 | + <el-button type="primary" icon="el-icon-search" @click="search()">查询</el-button> | |
| 27 | + <el-button icon="el-icon-refresh-right" @click="reset()">重置</el-button> | |
| 28 | + </el-form-item> | |
| 29 | + </el-col> | |
| 30 | + </el-form> | |
| 31 | + </el-row> | |
| 32 | + | |
| 33 | + <!-- 主要内容区域 --> | |
| 34 | + <div class="NCC-common-layout-main NCC-flex-main"> | |
| 35 | + <!-- 操作按钮区域 --> | |
| 36 | + <div class="NCC-common-head"> | |
| 37 | + <div> | |
| 38 | + <el-button type="primary" icon="el-icon-plus" @click="addClassHandle()">创建班级</el-button> | |
| 39 | + <el-button type="success" icon="el-icon-user" | |
| 40 | + @click="addStudentHandle()">添加学员(需选择班级)</el-button> | |
| 41 | + <el-button type="text" icon="el-icon-download" @click="exportData()">导出</el-button> | |
| 42 | + </div> | |
| 43 | + <div class="NCC-common-head-right"> | |
| 44 | + <el-tooltip effect="dark" content="刷新" placement="top"> | |
| 45 | + <el-link icon="icon-ym icon-ym-Refresh NCC-common-head-icon" :underline="false" | |
| 46 | + @click="reset()" /> | |
| 47 | + </el-tooltip> | |
| 48 | + <screenfull isContainer /> | |
| 49 | + </div> | |
| 50 | + </div> | |
| 51 | + | |
| 52 | + <!-- 班级列表表格 --> | |
| 53 | + <NCC-table v-loading="listLoading" :data="list" has-c @selection-change="handleSelectionChange"> | |
| 54 | + <el-table-column prop="className" label="班级名称" align="left" show-overflow-tooltip /> | |
| 55 | + <el-table-column prop="teacherName" label="授课老师" align="left" show-overflow-tooltip /> | |
| 56 | + <el-table-column prop="startTime" label="开始时间" align="center" width="120"> | |
| 57 | + <template slot-scope="scope"> | |
| 58 | + {{ scope.row.startTime | dateFormat }} | |
| 59 | + </template> | |
| 60 | + </el-table-column> | |
| 61 | + <el-table-column prop="endTime" label="结束时间" align="center" width="120"> | |
| 62 | + <template slot-scope="scope"> | |
| 63 | + {{ scope.row.endTime | dateFormat }} | |
| 64 | + </template> | |
| 65 | + </el-table-column> | |
| 66 | + <el-table-column prop="remark" label="备注" align="left" show-overflow-tooltip /> | |
| 67 | + <el-table-column label="操作" align="center" width="200" fixed="right"> | |
| 68 | + <template slot-scope="scope"> | |
| 69 | + <el-button type="text" @click="viewStudents(scope.row)">查看学员</el-button> | |
| 70 | + <el-button type="text" @click="addStudentsToClass(scope.row)">添加学员</el-button> | |
| 71 | + <el-button type="text" @click="viewStudyRecords(scope.row)">学习记录</el-button> | |
| 72 | + </template> | |
| 73 | + </el-table-column> | |
| 74 | + </NCC-table> | |
| 75 | + | |
| 76 | + <!-- 分页组件 --> | |
| 77 | + <pagination v-show="total > 0" :total="total" :page.sync="query.currentPage" | |
| 78 | + :limit.sync="query.pageSize" @pagination="getList" /> | |
| 79 | + </div> | |
| 80 | + </div> | |
| 81 | + | |
| 82 | + <!-- 创建班级弹窗 --> | |
| 83 | + <CreateClassForm v-if="createClassVisible" ref="CreateClassForm" @refreshDataList="getList" /> | |
| 84 | + | |
| 85 | + <!-- 添加学员弹窗 --> | |
| 86 | + <AddStudentForm v-if="addStudentVisible" ref="AddStudentForm" @refreshDataList="getList" /> | |
| 87 | + | |
| 88 | + <!-- 学员列表弹窗 --> | |
| 89 | + <StudentListDialog v-if="studentListVisible" ref="StudentListDialog" | |
| 90 | + @viewStudyRecords="handleViewStudyRecords" /> | |
| 91 | + | |
| 92 | + <!-- 学习记录弹窗 --> | |
| 93 | + <StudyRecordDialog v-if="studyRecordVisible" ref="StudyRecordDialog" /> | |
| 94 | + </div> | |
| 95 | +</template> | |
| 96 | + | |
| 97 | +<script> | |
| 98 | +import { getClassList } from '@/api/extend/lqStudyClass' | |
| 99 | +import CreateClassForm from './CreateClassForm' | |
| 100 | +import AddStudentForm from './AddStudentForm' | |
| 101 | +import StudentListDialog from './StudentListDialog' | |
| 102 | +import StudyRecordDialog from './StudyRecordDialog' | |
| 103 | +import Pagination from '@/components/Pagination' | |
| 104 | + | |
| 105 | +export default { | |
| 106 | + name: 'LqStudyClass', | |
| 107 | + components: { | |
| 108 | + CreateClassForm, | |
| 109 | + AddStudentForm, | |
| 110 | + StudentListDialog, | |
| 111 | + StudyRecordDialog, | |
| 112 | + Pagination | |
| 113 | + }, | |
| 114 | + data() { | |
| 115 | + return { | |
| 116 | + query: { | |
| 117 | + className: '', | |
| 118 | + teacherId: '', | |
| 119 | + startTime: '', | |
| 120 | + endTime: '', | |
| 121 | + currentPage: 1, | |
| 122 | + pageSize: 20 | |
| 123 | + }, | |
| 124 | + list: [], | |
| 125 | + total: 0, | |
| 126 | + listLoading: true, | |
| 127 | + createClassVisible: false, | |
| 128 | + addStudentVisible: false, | |
| 129 | + studentListVisible: false, | |
| 130 | + studyRecordVisible: false, | |
| 131 | + selectedIds: [] | |
| 132 | + } | |
| 133 | + }, | |
| 134 | + created() { | |
| 135 | + this.getList() | |
| 136 | + }, | |
| 137 | + methods: { | |
| 138 | + // 获取班级列表 | |
| 139 | + getList() { | |
| 140 | + this.listLoading = true | |
| 141 | + getClassList(this.query).then(response => { | |
| 142 | + if (response.code === 200) { | |
| 143 | + this.list = response.data.list || [] | |
| 144 | + this.total = (response.data.pagination && response.data.pagination.total) || 0 | |
| 145 | + } else { | |
| 146 | + this.$message.error(response.msg || '获取班级列表失败') | |
| 147 | + } | |
| 148 | + this.listLoading = false | |
| 149 | + }).catch(() => { | |
| 150 | + this.listLoading = false | |
| 151 | + }) | |
| 152 | + }, | |
| 153 | + | |
| 154 | + // 搜索 | |
| 155 | + search() { | |
| 156 | + this.query.currentPage = 1 | |
| 157 | + this.getList() | |
| 158 | + }, | |
| 159 | + | |
| 160 | + // 重置 | |
| 161 | + reset() { | |
| 162 | + this.query = { | |
| 163 | + className: '', | |
| 164 | + teacherId: '', | |
| 165 | + startTime: '', | |
| 166 | + endTime: '', | |
| 167 | + currentPage: 1, | |
| 168 | + pageSize: 20 | |
| 169 | + } | |
| 170 | + this.getList() | |
| 171 | + }, | |
| 172 | + | |
| 173 | + // 创建班级 | |
| 174 | + addClassHandle() { | |
| 175 | + this.createClassVisible = true | |
| 176 | + this.$nextTick(() => { | |
| 177 | + this.$refs.CreateClassForm.init() | |
| 178 | + }) | |
| 179 | + }, | |
| 180 | + | |
| 181 | + // 添加学员 | |
| 182 | + addStudentHandle() { | |
| 183 | + if (this.selectedIds.length === 0) { | |
| 184 | + this.$message.warning('请先选择一个班级') | |
| 185 | + return | |
| 186 | + } | |
| 187 | + if (this.selectedIds.length > 1) { | |
| 188 | + this.$message.warning('请只选择一个班级') | |
| 189 | + return | |
| 190 | + } | |
| 191 | + | |
| 192 | + // 找到选中的班级信息 | |
| 193 | + const selectedClass = this.list.find(item => item.id === this.selectedIds[0]) | |
| 194 | + if (selectedClass) { | |
| 195 | + this.addStudentVisible = true | |
| 196 | + this.$nextTick(() => { | |
| 197 | + this.$refs.AddStudentForm.init(selectedClass.id, selectedClass.className) | |
| 198 | + }) | |
| 199 | + } | |
| 200 | + }, | |
| 201 | + | |
| 202 | + // 向班级添加学员 | |
| 203 | + addStudentsToClass(row) { | |
| 204 | + this.addStudentVisible = true | |
| 205 | + this.$nextTick(() => { | |
| 206 | + this.$refs.AddStudentForm.init(row.id, row.className) | |
| 207 | + }) | |
| 208 | + }, | |
| 209 | + | |
| 210 | + // 查看学员 | |
| 211 | + viewStudents(row) { | |
| 212 | + this.studentListVisible = true | |
| 213 | + this.$nextTick(() => { | |
| 214 | + this.$refs.StudentListDialog.init(row.id, row.className) | |
| 215 | + }) | |
| 216 | + }, | |
| 217 | + | |
| 218 | + // 查看学习记录 | |
| 219 | + viewStudyRecords(row) { | |
| 220 | + this.studyRecordVisible = true | |
| 221 | + this.$nextTick(() => { | |
| 222 | + this.$refs.StudyRecordDialog.init(row.id, row.className) | |
| 223 | + }) | |
| 224 | + }, | |
| 225 | + | |
| 226 | + // 多选 | |
| 227 | + handleSelectionChange(selection) { | |
| 228 | + this.selectedIds = selection.map(item => item.id) | |
| 229 | + }, | |
| 230 | + | |
| 231 | + // 处理学员列表中的学习记录查看 | |
| 232 | + handleViewStudyRecords(row) { | |
| 233 | + this.studyRecordVisible = true | |
| 234 | + this.$nextTick(() => { | |
| 235 | + this.$refs.StudyRecordDialog.init(row.classId, row.className) | |
| 236 | + }) | |
| 237 | + }, | |
| 238 | + } | |
| 239 | +} | |
| 240 | +</script> | |
| 241 | + | |
| 242 | +<style scoped> | |
| 243 | +.NCC-common-layout { | |
| 244 | + height: 100%; | |
| 245 | +} | |
| 246 | +</style> | ... | ... |
antis-ncc-admin/src/views/lqStudyClass/test.html
0 → 100644
| 1 | +<!DOCTYPE html> | |
| 2 | +<html lang="zh-CN"> | |
| 3 | +<head> | |
| 4 | + <meta charset="UTF-8"> | |
| 5 | + <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| 6 | + <title>学习班级管理 - 功能测试</title> | |
| 7 | + <style> | |
| 8 | + body { | |
| 9 | + font-family: 'Microsoft YaHei', Arial, sans-serif; | |
| 10 | + margin: 0; | |
| 11 | + padding: 20px; | |
| 12 | + background-color: #f5f7fa; | |
| 13 | + } | |
| 14 | + .container { | |
| 15 | + max-width: 1200px; | |
| 16 | + margin: 0 auto; | |
| 17 | + background: white; | |
| 18 | + border-radius: 8px; | |
| 19 | + padding: 30px; | |
| 20 | + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1); | |
| 21 | + } | |
| 22 | + .header { | |
| 23 | + text-align: center; | |
| 24 | + margin-bottom: 30px; | |
| 25 | + padding-bottom: 20px; | |
| 26 | + border-bottom: 2px solid #409EFF; | |
| 27 | + } | |
| 28 | + .header h1 { | |
| 29 | + color: #409EFF; | |
| 30 | + margin: 0; | |
| 31 | + font-size: 28px; | |
| 32 | + } | |
| 33 | + .header p { | |
| 34 | + color: #666; | |
| 35 | + margin: 10px 0 0 0; | |
| 36 | + font-size: 16px; | |
| 37 | + } | |
| 38 | + .feature-section { | |
| 39 | + margin-bottom: 30px; | |
| 40 | + } | |
| 41 | + .feature-section h2 { | |
| 42 | + color: #303133; | |
| 43 | + border-left: 4px solid #409EFF; | |
| 44 | + padding-left: 15px; | |
| 45 | + margin-bottom: 20px; | |
| 46 | + } | |
| 47 | + .feature-list { | |
| 48 | + list-style: none; | |
| 49 | + padding: 0; | |
| 50 | + } | |
| 51 | + .feature-list li { | |
| 52 | + padding: 10px 0; | |
| 53 | + border-bottom: 1px solid #ebeef5; | |
| 54 | + position: relative; | |
| 55 | + padding-left: 25px; | |
| 56 | + } | |
| 57 | + .feature-list li:before { | |
| 58 | + content: "✓"; | |
| 59 | + position: absolute; | |
| 60 | + left: 0; | |
| 61 | + color: #67C23A; | |
| 62 | + font-weight: bold; | |
| 63 | + } | |
| 64 | + .api-info { | |
| 65 | + background: #f8f9fa; | |
| 66 | + padding: 20px; | |
| 67 | + border-radius: 6px; | |
| 68 | + margin: 20px 0; | |
| 69 | + } | |
| 70 | + .api-info h3 { | |
| 71 | + color: #409EFF; | |
| 72 | + margin-top: 0; | |
| 73 | + } | |
| 74 | + .code-block { | |
| 75 | + background: #2d3748; | |
| 76 | + color: #e2e8f0; | |
| 77 | + padding: 15px; | |
| 78 | + border-radius: 4px; | |
| 79 | + font-family: 'Courier New', monospace; | |
| 80 | + font-size: 14px; | |
| 81 | + overflow-x: auto; | |
| 82 | + margin: 10px 0; | |
| 83 | + } | |
| 84 | + .file-structure { | |
| 85 | + background: #f8f9fa; | |
| 86 | + padding: 15px; | |
| 87 | + border-radius: 4px; | |
| 88 | + font-family: 'Courier New', monospace; | |
| 89 | + font-size: 14px; | |
| 90 | + margin: 10px 0; | |
| 91 | + } | |
| 92 | + .status-badge { | |
| 93 | + display: inline-block; | |
| 94 | + padding: 4px 8px; | |
| 95 | + border-radius: 12px; | |
| 96 | + font-size: 12px; | |
| 97 | + font-weight: bold; | |
| 98 | + margin-left: 10px; | |
| 99 | + } | |
| 100 | + .status-completed { | |
| 101 | + background: #f0f9ff; | |
| 102 | + color: #27ae60; | |
| 103 | + } | |
| 104 | + .status-pending { | |
| 105 | + background: #fff7e6; | |
| 106 | + color: #e6a23c; | |
| 107 | + } | |
| 108 | + </style> | |
| 109 | +</head> | |
| 110 | +<body> | |
| 111 | + <div class="container"> | |
| 112 | + <div class="header"> | |
| 113 | + <h1>🎓 学习班级管理系统</h1> | |
| 114 | + <p>基于Vue 2.6 + Element UI + ASP.NET Core开发的学习班级管理功能</p> | |
| 115 | + </div> | |
| 116 | + | |
| 117 | + <div class="feature-section"> | |
| 118 | + <h2>📋 已实现功能</h2> | |
| 119 | + <ul class="feature-list"> | |
| 120 | + <li>学习班级列表展示(支持分页、搜索、筛选)</li> | |
| 121 | + <li>创建学习班级并添加学员(批量添加)</li> | |
| 122 | + <li>向现有班级添加学员</li> | |
| 123 | + <li>查看班级下所有学员信息(分页展示)</li> | |
| 124 | + <li>学习记录管理(添加、查看、作废)</li> | |
| 125 | + <li>学员信息搜索和筛选</li> | |
| 126 | + <li>学习记录搜索和筛选</li> | |
| 127 | + <li>响应式布局设计</li> | |
| 128 | + <li>表单验证和错误处理</li> | |
| 129 | + </ul> | |
| 130 | + </div> | |
| 131 | + | |
| 132 | + <div class="feature-section"> | |
| 133 | + <h2>🔧 API接口配置</h2> | |
| 134 | + <div class="api-info"> | |
| 135 | + <h3>接口地址:<code>/api/Extend/LqStudyClass</code></h3> | |
| 136 | + <p><strong>请求方法:</strong>GET, POST</p> | |
| 137 | + <p><strong>主要接口:</strong></p> | |
| 138 | + <div class="code-block"> | |
| 139 | +POST /api/Extend/LqStudyClass/CreateClassWithStudents | |
| 140 | +GET /api/Extend/LqStudyClass/GetClassList | |
| 141 | +POST /api/Extend/LqStudyClass/AddStudentsToClass | |
| 142 | +GET /api/Extend/LqStudyClass/GetStudentListByClassId | |
| 143 | +POST /api/Extend/LqStudyClass/AddStudyRecord | |
| 144 | +GET /api/Extend/LqStudyClass/GetStudyRecordList | |
| 145 | +POST /api/Extend/LqStudyClass/CancelStudyRecord | |
| 146 | + </div> | |
| 147 | + </div> | |
| 148 | + </div> | |
| 149 | + | |
| 150 | + <div class="feature-section"> | |
| 151 | + <h2>📁 文件结构</h2> | |
| 152 | + <div class="file-structure"> | |
| 153 | +lqStudyClass/ | |
| 154 | +├── index.vue # 主页面组件 | |
| 155 | +├── CreateClassForm.vue # 创建班级表单组件 | |
| 156 | +├── AddStudentForm.vue # 添加学员表单组件 | |
| 157 | +├── StudentListDialog.vue # 学员列表弹窗组件 | |
| 158 | +└── StudyRecordDialog.vue # 学习记录管理弹窗组件 | |
| 159 | + | |
| 160 | +api/extend/ | |
| 161 | +└── lqStudyClass.js # API接口定义 | |
| 162 | + </div> | |
| 163 | + </div> | |
| 164 | + | |
| 165 | + <div class="feature-section"> | |
| 166 | + <h2>🎯 核心功能说明</h2> | |
| 167 | + <div class="api-info"> | |
| 168 | + <h3>1. 创建学习班级并添加学员</h3> | |
| 169 | + <p>支持一次性创建班级并批量添加学员,包含班级基本信息(名称、老师、时间、备注)和学员详细信息(姓名、手机、员工ID、入学时间、HR归属)。</p> | |
| 170 | + | |
| 171 | + <h3>2. 班级列表管理</h3> | |
| 172 | + <p>提供班级列表展示,支持按班级名称、授课老师、开始时间等条件搜索,支持分页显示。</p> | |
| 173 | + | |
| 174 | + <h3>3. 学员管理</h3> | |
| 175 | + <p>可以查看指定班级下的所有学员信息,支持学员信息搜索和筛选,支持向现有班级添加新学员。</p> | |
| 176 | + | |
| 177 | + <h3>4. 学习记录管理</h3> | |
| 178 | + <p>记录学员的学习情况,包括学习类型、交通费、学习日期、日常状态、是否下店协助等信息,支持记录的添加、查看和作废操作。</p> | |
| 179 | + </div> | |
| 180 | + </div> | |
| 181 | + | |
| 182 | + <div class="feature-section"> | |
| 183 | + <h2>🚀 技术特点</h2> | |
| 184 | + <ul class="feature-list"> | |
| 185 | + <li>采用Vue 2.6 + Element UI技术栈</li> | |
| 186 | + <li>组件化开发,模块清晰</li> | |
| 187 | + <li>统一的API调用方式</li> | |
| 188 | + <li>完善的表单验证</li> | |
| 189 | + <li>响应式设计</li> | |
| 190 | + <li>良好的用户体验</li> | |
| 191 | + <li>符合项目开发规范</li> | |
| 192 | + </ul> | |
| 193 | + </div> | |
| 194 | + | |
| 195 | + <div class="feature-section"> | |
| 196 | + <h2>📊 数据流程</h2> | |
| 197 | + <div class="api-info"> | |
| 198 | + <p><strong>创建班级流程:</strong></p> | |
| 199 | + <ol> | |
| 200 | + <li>填写班级基本信息(名称、老师、时间等)</li> | |
| 201 | + <li>添加学员信息(可批量添加)</li> | |
| 202 | + <li>提交表单,后端创建班级和学员记录</li> | |
| 203 | + <li>返回创建结果,刷新列表</li> | |
| 204 | + </ol> | |
| 205 | + | |
| 206 | + <p><strong>学员管理流程:</strong></p> | |
| 207 | + <ol> | |
| 208 | + <li>从班级列表选择班级</li> | |
| 209 | + <li>查看班级学员列表</li> | |
| 210 | + <li>支持搜索和筛选学员</li> | |
| 211 | + <li>可添加新学员到班级</li> | |
| 212 | + </ol> | |
| 213 | + | |
| 214 | + <p><strong>学习记录流程:</strong></p> | |
| 215 | + <ol> | |
| 216 | + <li>选择班级查看学习记录</li> | |
| 217 | + <li>添加新的学习记录</li> | |
| 218 | + <li>支持记录搜索和筛选</li> | |
| 219 | + <li>可作废无效记录</li> | |
| 220 | + </ol> | |
| 221 | + </div> | |
| 222 | + </div> | |
| 223 | + | |
| 224 | + <div class="feature-section"> | |
| 225 | + <h2>✅ 开发状态</h2> | |
| 226 | + <p> | |
| 227 | + <strong>前端页面:</strong><span class="status-badge status-completed">已完成</span> | |
| 228 | + <strong>API接口:</strong><span class="status-badge status-completed">已对接</span> | |
| 229 | + <strong>路由配置:</strong><span class="status-badge status-completed">已配置</span> | |
| 230 | + <strong>功能测试:</strong><span class="status-badge status-pending">待测试</span> | |
| 231 | + </p> | |
| 232 | + </div> | |
| 233 | + </div> | |
| 234 | +</body> | |
| 235 | +</html> | |
| 236 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqInventoryService.cs
| ... | ... | @@ -171,20 +171,40 @@ namespace NCC.Extend |
| 171 | 171 | quantity = x.Quantity, |
| 172 | 172 | productCategory = x.ProductCategory, |
| 173 | 173 | departmentId = x.DepartmentId, |
| 174 | - departmentName = SqlFunc.Subqueryable<UserEntity>().Where(u => u.Id == x.DepartmentId).Select(u => u.RealName), | |
| 174 | + departmentName = "", | |
| 175 | 175 | standardUnit = x.StandardUnit, |
| 176 | 176 | totalValue = x.Price * x.Quantity, |
| 177 | 177 | createUser = x.CreateUser, |
| 178 | - createUserName = SqlFunc.Subqueryable<UserEntity>().Where(u => u.Id == x.CreateUser).Select(u => u.RealName), | |
| 178 | + createUserName = "", | |
| 179 | 179 | createTime = x.CreateTime, |
| 180 | 180 | updateUser = x.UpdateUser, |
| 181 | - updateUserName = SqlFunc.Subqueryable<UserEntity>().Where(u => u.Id == x.UpdateUser).Select(u => u.RealName), | |
| 181 | + updateUserName = "", | |
| 182 | 182 | updateTime = x.UpdateTime, |
| 183 | 183 | isEffective = x.IsEffective |
| 184 | 184 | }) |
| 185 | 185 | .MergeTable() |
| 186 | 186 | .OrderBy(sidx + " " + input.sort) |
| 187 | 187 | .ToPagedListAsync(input.currentPage, input.pageSize); |
| 188 | + | |
| 189 | + // 补充用户名称信息 | |
| 190 | + foreach (var item in data.list) | |
| 191 | + { | |
| 192 | + if (!string.IsNullOrEmpty(item.departmentId)) | |
| 193 | + { | |
| 194 | + var deptUser = await _db.Queryable<UserEntity>().Where(u => u.Id == item.departmentId).FirstAsync(); | |
| 195 | + item.departmentName = deptUser?.RealName ?? ""; | |
| 196 | + } | |
| 197 | + if (!string.IsNullOrEmpty(item.createUser)) | |
| 198 | + { | |
| 199 | + var createUser = await _db.Queryable<UserEntity>().Where(u => u.Id == item.createUser).FirstAsync(); | |
| 200 | + item.createUserName = createUser?.RealName ?? ""; | |
| 201 | + } | |
| 202 | + if (!string.IsNullOrEmpty(item.updateUser)) | |
| 203 | + { | |
| 204 | + var updateUser = await _db.Queryable<UserEntity>().Where(u => u.Id == item.updateUser).FirstAsync(); | |
| 205 | + item.updateUserName = updateUser?.RealName ?? ""; | |
| 206 | + } | |
| 207 | + } | |
| 188 | 208 | return PageResult<LqInventoryListOutput>.SqlSugarPageResult(data); |
| 189 | 209 | } |
| 190 | 210 | catch (Exception ex) |
| ... | ... | @@ -210,12 +230,14 @@ namespace NCC.Extend |
| 210 | 230 | { |
| 211 | 231 | throw NCCException.Oh("库存ID不能为空"); |
| 212 | 232 | } |
| 233 | + // 查询库存信息 | |
| 213 | 234 | var inventory = await _db.Queryable<LqInventoryEntity>().Where(x => x.Id == id).FirstAsync(); |
| 214 | 235 | |
| 215 | 236 | if (inventory == null) |
| 216 | 237 | { |
| 217 | 238 | throw NCCException.Oh("库存记录不存在"); |
| 218 | 239 | } |
| 240 | + | |
| 219 | 241 | var result = new LqInventoryInfoOutput |
| 220 | 242 | { |
| 221 | 243 | id = inventory.Id, |
| ... | ... | @@ -224,18 +246,35 @@ namespace NCC.Extend |
| 224 | 246 | quantity = inventory.Quantity, |
| 225 | 247 | productCategory = inventory.ProductCategory, |
| 226 | 248 | departmentId = inventory.DepartmentId, |
| 227 | - departmentName = SqlFunc.Subqueryable<UserEntity>().Where(u => u.Id == inventory.DepartmentId).Select(u => u.RealName), | |
| 249 | + departmentName = "", | |
| 228 | 250 | standardUnit = inventory.StandardUnit, |
| 229 | 251 | totalValue = inventory.Price * inventory.Quantity, |
| 230 | 252 | createUser = inventory.CreateUser, |
| 231 | - createUserName = SqlFunc.Subqueryable<UserEntity>().Where(u => u.Id == inventory.CreateUser).Select(u => u.RealName), | |
| 253 | + createUserName = "", | |
| 232 | 254 | createTime = inventory.CreateTime, |
| 233 | 255 | updateUser = inventory.UpdateUser, |
| 234 | - updateUserName = SqlFunc.Subqueryable<UserEntity>().Where(u => u.Id == inventory.UpdateUser).Select(u => u.RealName), | |
| 256 | + updateUserName = "", | |
| 235 | 257 | updateTime = inventory.UpdateTime, |
| 236 | 258 | isEffective = inventory.IsEffective |
| 237 | 259 | }; |
| 238 | 260 | |
| 261 | + // 补充用户名称信息 | |
| 262 | + if (!string.IsNullOrEmpty(result.departmentId)) | |
| 263 | + { | |
| 264 | + var deptUser = await _db.Queryable<UserEntity>().Where(u => u.Id == result.departmentId).FirstAsync(); | |
| 265 | + result.departmentName = deptUser?.RealName ?? ""; | |
| 266 | + } | |
| 267 | + if (!string.IsNullOrEmpty(result.createUser)) | |
| 268 | + { | |
| 269 | + var createUser = await _db.Queryable<UserEntity>().Where(u => u.Id == result.createUser).FirstAsync(); | |
| 270 | + result.createUserName = createUser?.RealName ?? ""; | |
| 271 | + } | |
| 272 | + if (!string.IsNullOrEmpty(result.updateUser)) | |
| 273 | + { | |
| 274 | + var updateUser = await _db.Queryable<UserEntity>().Where(u => u.Id == result.updateUser).FirstAsync(); | |
| 275 | + result.updateUserName = updateUser?.RealName ?? ""; | |
| 276 | + } | |
| 277 | + | |
| 239 | 278 | return result; |
| 240 | 279 | } |
| 241 | 280 | catch (Exception ex) | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqInventoryUsageService.cs
| ... | ... | @@ -186,28 +186,70 @@ namespace NCC.Extend |
| 186 | 186 | { |
| 187 | 187 | Id = x.Id, |
| 188 | 188 | ProductId = x.ProductId, |
| 189 | - ProductName = SqlFunc.Subqueryable<LqInventoryEntity>().Where(p => p.Id == x.ProductId).Select(p => p.ProductName), | |
| 190 | - ProductCategory = SqlFunc.Subqueryable<LqInventoryEntity>().Where(p => p.Id == x.ProductId).Select(p => p.ProductCategory), | |
| 191 | - ProductPrice = SqlFunc.Subqueryable<LqInventoryEntity>().Where(p => p.Id == x.ProductId).Select(p => p.Price), | |
| 189 | + ProductName = "", | |
| 190 | + ProductCategory = "", | |
| 191 | + ProductPrice = 0, | |
| 192 | 192 | StoreId = x.StoreId, |
| 193 | - StoreName = SqlFunc.Subqueryable<UserEntity>().Where(u => u.Id == x.StoreId).Select(u => u.RealName), | |
| 193 | + StoreName = "", | |
| 194 | 194 | UsageTime = x.UsageTime, |
| 195 | 195 | UsageQuantity = x.UsageQuantity, |
| 196 | 196 | RelatedConsumeId = x.RelatedConsumeId, |
| 197 | 197 | CreateUser = x.CreateUser, |
| 198 | - CreateUserName = SqlFunc.Subqueryable<UserEntity>().Where(u => u.Id == x.CreateUser).Select(u => u.RealName), | |
| 198 | + CreateUserName = "", | |
| 199 | 199 | CreateTime = x.CreateTime, |
| 200 | 200 | UpdateUser = x.UpdateUser, |
| 201 | - UpdateUserName = SqlFunc.Subqueryable<UserEntity>().Where(u => u.Id == x.UpdateUser).Select(u => u.RealName), | |
| 201 | + UpdateUserName = "", | |
| 202 | 202 | UpdateTime = x.UpdateTime, |
| 203 | 203 | IsEffective = x.IsEffective |
| 204 | 204 | }) |
| 205 | 205 | .MergeTable() |
| 206 | - .WhereIF(!string.IsNullOrWhiteSpace(input.ProductName), x => x.ProductName.Contains(input.ProductName)) | |
| 207 | - .WhereIF(!string.IsNullOrWhiteSpace(input.ProductCategory), x => x.ProductCategory.Contains(input.ProductCategory)) | |
| 208 | - .WhereIF(!string.IsNullOrWhiteSpace(input.StoreName), x => x.StoreName.Contains(input.StoreName)) | |
| 209 | 206 | .OrderBy(sidx + " " + input.sort) |
| 210 | 207 | .ToPagedListAsync(input.currentPage, input.pageSize); |
| 208 | + | |
| 209 | + // 补充产品信息和用户信息 | |
| 210 | + foreach (var item in data.list) | |
| 211 | + { | |
| 212 | + if (!string.IsNullOrEmpty(item.ProductId)) | |
| 213 | + { | |
| 214 | + var product = await _db.Queryable<LqInventoryEntity>().Where(p => p.Id == item.ProductId).FirstAsync(); | |
| 215 | + if (product != null) | |
| 216 | + { | |
| 217 | + item.ProductName = product.ProductName; | |
| 218 | + item.ProductCategory = product.ProductCategory; | |
| 219 | + item.ProductPrice = product.Price; | |
| 220 | + } | |
| 221 | + } | |
| 222 | + if (!string.IsNullOrEmpty(item.StoreId)) | |
| 223 | + { | |
| 224 | + var store = await _db.Queryable<UserEntity>().Where(u => u.Id == item.StoreId).FirstAsync(); | |
| 225 | + item.StoreName = store?.RealName ?? ""; | |
| 226 | + } | |
| 227 | + if (!string.IsNullOrEmpty(item.CreateUser)) | |
| 228 | + { | |
| 229 | + var createUser = await _db.Queryable<UserEntity>().Where(u => u.Id == item.CreateUser).FirstAsync(); | |
| 230 | + item.CreateUserName = createUser?.RealName ?? ""; | |
| 231 | + } | |
| 232 | + if (!string.IsNullOrEmpty(item.UpdateUser)) | |
| 233 | + { | |
| 234 | + var updateUser = await _db.Queryable<UserEntity>().Where(u => u.Id == item.UpdateUser).FirstAsync(); | |
| 235 | + item.UpdateUserName = updateUser?.RealName ?? ""; | |
| 236 | + } | |
| 237 | + } | |
| 238 | + | |
| 239 | + // 应用产品名称和分类的过滤条件 | |
| 240 | + if (!string.IsNullOrWhiteSpace(input.ProductName) || !string.IsNullOrWhiteSpace(input.ProductCategory)) | |
| 241 | + { | |
| 242 | + data.list = data.list.Where(x => | |
| 243 | + (string.IsNullOrWhiteSpace(input.ProductName) || x.ProductName.Contains(input.ProductName)) && | |
| 244 | + (string.IsNullOrWhiteSpace(input.ProductCategory) || x.ProductCategory.Contains(input.ProductCategory)) | |
| 245 | + ).ToList(); | |
| 246 | + } | |
| 247 | + | |
| 248 | + // 应用门店名称的过滤条件 | |
| 249 | + if (!string.IsNullOrWhiteSpace(input.StoreName)) | |
| 250 | + { | |
| 251 | + data.list = data.list.Where(x => x.StoreName.Contains(input.StoreName)).ToList(); | |
| 252 | + } | |
| 211 | 253 | return PageResult<LqInventoryUsageListOutput>.SqlSugarPageResult(data); |
| 212 | 254 | } |
| 213 | 255 | catch (Exception ex) | ... | ... |