Commit 873e7a7d474b004bafe0bd0cda64a6e3165e60f2
Merge branch 'master' of http://39.98.150.180/antissoft/lvqianmeiye_ERP
Showing
37 changed files
with
9891 additions
and
467 deletions
antis-ncc-admin/src/views/LqLaundryFlow/detail-dialog.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog title="流水详情" :visible.sync="visible" width="800px" :close-on-click-modal="false"> | |
| 3 | + <div v-loading="loading" class="detail-container"> | |
| 4 | + <!-- 基本信息 --> | |
| 5 | + <div class="detail-section"> | |
| 6 | + <div class="section-title"> | |
| 7 | + <i class="el-icon-info section-icon"></i> | |
| 8 | + <span>基本信息</span> | |
| 9 | + </div> | |
| 10 | + <div class="detail-content"> | |
| 11 | + <div class="detail-row"> | |
| 12 | + <div class="detail-label"> | |
| 13 | + <i class="el-icon-upload2 detail-icon" :class="form.flowType === 0 ? 'send-icon' : 'return-icon'"></i> | |
| 14 | + <span>流水类型</span> | |
| 15 | + </div> | |
| 16 | + <div class="detail-value"> | |
| 17 | + <el-tag :type="form.flowType === 0 ? 'primary' : 'success'" size="small"> | |
| 18 | + <i :class="form.flowType === 0 ? 'el-icon-upload2' : 'el-icon-download'" style="margin-right: 4px;"></i> | |
| 19 | + {{ form.flowTypeName || '无' }} | |
| 20 | + </el-tag> | |
| 21 | + </div> | |
| 22 | + </div> | |
| 23 | + <div class="detail-row"> | |
| 24 | + <div class="detail-label"> | |
| 25 | + <i class="el-icon-tickets detail-icon batch-icon"></i> | |
| 26 | + <span>批次号</span> | |
| 27 | + </div> | |
| 28 | + <div class="detail-value"> | |
| 29 | + <span>{{ form.batchNumber || '无' }}</span> | |
| 30 | + </div> | |
| 31 | + </div> | |
| 32 | + <div class="detail-row"> | |
| 33 | + <div class="detail-label"> | |
| 34 | + <i class="el-icon-office-building detail-icon store-icon"></i> | |
| 35 | + <span>门店名称</span> | |
| 36 | + </div> | |
| 37 | + <div class="detail-value"> | |
| 38 | + <span>{{ form.storeName || '无' }}</span> | |
| 39 | + </div> | |
| 40 | + </div> | |
| 41 | + <div class="detail-row"> | |
| 42 | + <div class="detail-label"> | |
| 43 | + <i class="el-icon-goods detail-icon product-icon"></i> | |
| 44 | + <span>产品类型</span> | |
| 45 | + </div> | |
| 46 | + <div class="detail-value"> | |
| 47 | + <span>{{ form.productType || '无' }}</span> | |
| 48 | + </div> | |
| 49 | + </div> | |
| 50 | + <div class="detail-row"> | |
| 51 | + <div class="detail-label"> | |
| 52 | + <i class="el-icon-s-shop detail-icon supplier-icon"></i> | |
| 53 | + <span>清洗商名称</span> | |
| 54 | + </div> | |
| 55 | + <div class="detail-value"> | |
| 56 | + <span>{{ form.laundrySupplierName || '无' }}</span> | |
| 57 | + </div> | |
| 58 | + </div> | |
| 59 | + <div class="detail-row"> | |
| 60 | + <div class="detail-label"> | |
| 61 | + <i class="el-icon-s-data detail-icon quantity-icon"></i> | |
| 62 | + <span>数量</span> | |
| 63 | + </div> | |
| 64 | + <div class="detail-value"> | |
| 65 | + <span class="value-number">{{ form.quantity || 0 }}</span> | |
| 66 | + </div> | |
| 67 | + </div> | |
| 68 | + </div> | |
| 69 | + </div> | |
| 70 | + | |
| 71 | + <!-- 费用信息 --> | |
| 72 | + <div class="detail-section"> | |
| 73 | + <div class="section-title"> | |
| 74 | + <i class="el-icon-coin section-icon"></i> | |
| 75 | + <span>费用信息</span> | |
| 76 | + </div> | |
| 77 | + <div class="detail-content"> | |
| 78 | + <div class="detail-row"> | |
| 79 | + <div class="detail-label"> | |
| 80 | + <i class="el-icon-coin detail-icon price-icon"></i> | |
| 81 | + <span>清洗单价</span> | |
| 82 | + </div> | |
| 83 | + <div class="detail-value"> | |
| 84 | + <span class="value-number">¥{{ form.laundryPrice || 0 }}</span> | |
| 85 | + </div> | |
| 86 | + </div> | |
| 87 | + <div class="detail-row"> | |
| 88 | + <div class="detail-label"> | |
| 89 | + <i class="el-icon-money detail-icon total-price-icon"></i> | |
| 90 | + <span>总费用</span> | |
| 91 | + </div> | |
| 92 | + <div class="detail-value"> | |
| 93 | + <span class="value-number value-total">¥{{ form.totalPrice || 0 }}</span> | |
| 94 | + </div> | |
| 95 | + </div> | |
| 96 | + </div> | |
| 97 | + </div> | |
| 98 | + | |
| 99 | + <!-- 其他信息 --> | |
| 100 | + <div class="detail-section"> | |
| 101 | + <div class="section-title"> | |
| 102 | + <i class="el-icon-document section-icon"></i> | |
| 103 | + <span>其他信息</span> | |
| 104 | + </div> | |
| 105 | + <div class="detail-content"> | |
| 106 | + <div class="detail-row"> | |
| 107 | + <div class="detail-label"> | |
| 108 | + <i class="el-icon-document detail-icon remark-icon"></i> | |
| 109 | + <span>备注</span> | |
| 110 | + </div> | |
| 111 | + <div class="detail-value"> | |
| 112 | + <span>{{ form.remark || '无' }}</span> | |
| 113 | + </div> | |
| 114 | + </div> | |
| 115 | + <div class="detail-row"> | |
| 116 | + <div class="detail-label"> | |
| 117 | + <i class="el-icon-success detail-icon status-icon"></i> | |
| 118 | + <span>是否有效</span> | |
| 119 | + </div> | |
| 120 | + <div class="detail-value"> | |
| 121 | + <el-tag :type="form.isEffective === 1 ? 'success' : 'info'" size="small"> | |
| 122 | + {{ form.isEffective === 1 ? '有效' : '无效' }} | |
| 123 | + </el-tag> | |
| 124 | + </div> | |
| 125 | + </div> | |
| 126 | + <div class="detail-row"> | |
| 127 | + <div class="detail-label"> | |
| 128 | + <i class="el-icon-user detail-icon user-icon"></i> | |
| 129 | + <span>创建人</span> | |
| 130 | + </div> | |
| 131 | + <div class="detail-value"> | |
| 132 | + <span>{{ form.createUserName || '无' }}</span> | |
| 133 | + </div> | |
| 134 | + </div> | |
| 135 | + <div class="detail-row"> | |
| 136 | + <div class="detail-label"> | |
| 137 | + <i class="el-icon-time detail-icon time-icon"></i> | |
| 138 | + <span>创建时间</span> | |
| 139 | + </div> | |
| 140 | + <div class="detail-value"> | |
| 141 | + <span>{{ formatDateTime(form.createTime) || '无' }}</span> | |
| 142 | + </div> | |
| 143 | + </div> | |
| 144 | + </div> | |
| 145 | + </div> | |
| 146 | + </div> | |
| 147 | + <div slot="footer" class="dialog-footer"> | |
| 148 | + <el-button @click="visible = false">关闭</el-button> | |
| 149 | + </div> | |
| 150 | + </el-dialog> | |
| 151 | +</template> | |
| 152 | + | |
| 153 | +<script> | |
| 154 | +import request from '@/utils/request' | |
| 155 | + | |
| 156 | +export default { | |
| 157 | + name: 'DetailDialog', | |
| 158 | + data() { | |
| 159 | + return { | |
| 160 | + visible: false, | |
| 161 | + loading: false, | |
| 162 | + form: { | |
| 163 | + id: '', | |
| 164 | + flowType: 0, | |
| 165 | + flowTypeName: '', | |
| 166 | + batchNumber: '', | |
| 167 | + storeId: '', | |
| 168 | + storeName: '', | |
| 169 | + productType: '', | |
| 170 | + laundrySupplierId: '', | |
| 171 | + laundrySupplierName: '', | |
| 172 | + quantity: 0, | |
| 173 | + laundryPrice: 0, | |
| 174 | + totalPrice: 0, | |
| 175 | + remark: '', | |
| 176 | + isEffective: 1, | |
| 177 | + createUser: '', | |
| 178 | + createUserName: '', | |
| 179 | + createTime: '' | |
| 180 | + } | |
| 181 | + } | |
| 182 | + }, | |
| 183 | + methods: { | |
| 184 | + // 初始化 | |
| 185 | + init(id) { | |
| 186 | + this.visible = true | |
| 187 | + this.loading = true | |
| 188 | + request({ | |
| 189 | + url: `/api/Extend/LqLaundryFlow/${id}`, | |
| 190 | + method: 'GET' | |
| 191 | + }).then(res => { | |
| 192 | + this.loading = false | |
| 193 | + if (res.code == 200 && res.data) { | |
| 194 | + this.form = res.data | |
| 195 | + } else { | |
| 196 | + this.$message.error(res.msg || '获取详情失败') | |
| 197 | + this.visible = false | |
| 198 | + } | |
| 199 | + }).catch(() => { | |
| 200 | + this.loading = false | |
| 201 | + this.visible = false | |
| 202 | + }) | |
| 203 | + }, | |
| 204 | + // 格式化日期时间 | |
| 205 | + formatDateTime(dateTime) { | |
| 206 | + if (!dateTime) return '' | |
| 207 | + const date = new Date(dateTime) | |
| 208 | + const year = date.getFullYear() | |
| 209 | + const month = String(date.getMonth() + 1).padStart(2, '0') | |
| 210 | + const day = String(date.getDate()).padStart(2, '0') | |
| 211 | + const hours = String(date.getHours()).padStart(2, '0') | |
| 212 | + const minutes = String(date.getMinutes()).padStart(2, '0') | |
| 213 | + const seconds = String(date.getSeconds()).padStart(2, '0') | |
| 214 | + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}` | |
| 215 | + } | |
| 216 | + } | |
| 217 | +} | |
| 218 | +</script> | |
| 219 | + | |
| 220 | +<style lang="scss" scoped> | |
| 221 | +.detail-container { | |
| 222 | + padding: 0; | |
| 223 | +} | |
| 224 | + | |
| 225 | +.detail-section { | |
| 226 | + margin-bottom: 24px; | |
| 227 | + background: #fff; | |
| 228 | + border-radius: 8px; | |
| 229 | + overflow: hidden; | |
| 230 | + | |
| 231 | + &:last-child { | |
| 232 | + margin-bottom: 0; | |
| 233 | + } | |
| 234 | +} | |
| 235 | + | |
| 236 | +.section-title { | |
| 237 | + display: flex; | |
| 238 | + align-items: center; | |
| 239 | + padding: 16px 20px; | |
| 240 | + background: #f5f7fa; | |
| 241 | + border-bottom: 1px solid #e4e7ed; | |
| 242 | + font-size: 16px; | |
| 243 | + font-weight: 600; | |
| 244 | + color: #303133; | |
| 245 | +} | |
| 246 | + | |
| 247 | +.section-icon { | |
| 248 | + margin-right: 8px; | |
| 249 | + color: #409EFF; | |
| 250 | + font-size: 18px; | |
| 251 | +} | |
| 252 | + | |
| 253 | +.detail-content { | |
| 254 | + padding: 20px; | |
| 255 | +} | |
| 256 | + | |
| 257 | +.detail-row { | |
| 258 | + display: flex; | |
| 259 | + align-items: flex-start; | |
| 260 | + margin-bottom: 16px; | |
| 261 | + padding-bottom: 16px; | |
| 262 | + border-bottom: 1px solid #f0f2f5; | |
| 263 | + | |
| 264 | + &:last-child { | |
| 265 | + margin-bottom: 0; | |
| 266 | + padding-bottom: 0; | |
| 267 | + border-bottom: none; | |
| 268 | + } | |
| 269 | +} | |
| 270 | + | |
| 271 | +.detail-label { | |
| 272 | + display: flex; | |
| 273 | + align-items: center; | |
| 274 | + width: 140px; | |
| 275 | + flex-shrink: 0; | |
| 276 | + font-weight: 500; | |
| 277 | + color: #606266; | |
| 278 | +} | |
| 279 | + | |
| 280 | +.detail-icon { | |
| 281 | + margin-right: 8px; | |
| 282 | + font-size: 16px; | |
| 283 | +} | |
| 284 | + | |
| 285 | +.detail-value { | |
| 286 | + flex: 1; | |
| 287 | + color: #303133; | |
| 288 | + word-break: break-all; | |
| 289 | +} | |
| 290 | + | |
| 291 | +.value-number { | |
| 292 | + font-weight: 600; | |
| 293 | + color: #E6A23C; | |
| 294 | +} | |
| 295 | + | |
| 296 | +.value-total { | |
| 297 | + font-size: 18px; | |
| 298 | + color: #F56C6C; | |
| 299 | +} | |
| 300 | + | |
| 301 | +// 图标颜色 | |
| 302 | +.batch-icon { | |
| 303 | + color: #409EFF; | |
| 304 | +} | |
| 305 | + | |
| 306 | +.store-icon { | |
| 307 | + color: #409EFF; | |
| 308 | +} | |
| 309 | + | |
| 310 | +.product-icon { | |
| 311 | + color: #67C23A; | |
| 312 | +} | |
| 313 | + | |
| 314 | +.supplier-icon { | |
| 315 | + color: #409EFF; | |
| 316 | +} | |
| 317 | + | |
| 318 | +.quantity-icon { | |
| 319 | + color: #E6A23C; | |
| 320 | +} | |
| 321 | + | |
| 322 | +.price-icon { | |
| 323 | + color: #E6A23C; | |
| 324 | +} | |
| 325 | + | |
| 326 | +.total-price-icon { | |
| 327 | + color: #F56C6C; | |
| 328 | +} | |
| 329 | + | |
| 330 | +.remark-icon { | |
| 331 | + color: #909399; | |
| 332 | +} | |
| 333 | + | |
| 334 | +.status-icon { | |
| 335 | + color: #67C23A; | |
| 336 | +} | |
| 337 | + | |
| 338 | +.user-icon { | |
| 339 | + color: #909399; | |
| 340 | +} | |
| 341 | + | |
| 342 | +.time-icon { | |
| 343 | + color: #909399; | |
| 344 | +} | |
| 345 | + | |
| 346 | +.send-icon { | |
| 347 | + color: #409EFF; | |
| 348 | +} | |
| 349 | + | |
| 350 | +.return-icon { | |
| 351 | + color: #67C23A; | |
| 352 | +} | |
| 353 | + | |
| 354 | +.dialog-footer { | |
| 355 | + text-align: right; | |
| 356 | + padding-top: 20px; | |
| 357 | +} | |
| 358 | +</style> | |
| 359 | + | ... | ... |
antis-ncc-admin/src/views/LqLaundryFlow/difference-dialog.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog title="差异记录(送出数量 > 送回数量)" :visible.sync="visible" width="1200px" :close-on-click-modal="false"> | |
| 3 | + <!-- 查询条件 --> | |
| 4 | + <el-row class="search-box" :gutter="16"> | |
| 5 | + <el-form @submit.native.prevent label-width="90px"> | |
| 6 | + <el-col :span="8"> | |
| 7 | + <el-form-item label="门店"> | |
| 8 | + <el-select style="width: 100%" v-model="query.storeId" placeholder="请选择门店" clearable filterable> | |
| 9 | + <el-option v-for="item in storeList" :key="item.id" :label="item.fullName" :value="item.id" /> | |
| 10 | + </el-select> | |
| 11 | + </el-form-item> | |
| 12 | + </el-col> | |
| 13 | + <el-col :span="8"> | |
| 14 | + <el-form-item label="产品类型"> | |
| 15 | + <el-input v-model="query.productType" placeholder="请输入产品类型" clearable /> | |
| 16 | + </el-form-item> | |
| 17 | + </el-col> | |
| 18 | + <el-col :span="8"> | |
| 19 | + <el-form-item label="开始时间"> | |
| 20 | + <el-date-picker v-model="query.startTime" type="datetime" placeholder="选择开始时间" value-format="yyyy-MM-dd HH:mm:ss" style="width: 100%" /> | |
| 21 | + </el-form-item> | |
| 22 | + </el-col> | |
| 23 | + <el-col :span="8" style="margin-top: 20px;"> | |
| 24 | + <el-form-item label="结束时间"> | |
| 25 | + <el-date-picker v-model="query.endTime" type="datetime" placeholder="选择结束时间" value-format="yyyy-MM-dd HH:mm:ss" style="width: 100%" /> | |
| 26 | + </el-form-item> | |
| 27 | + </el-col> | |
| 28 | + <el-col :span="8" style="margin-top: 20px;"> | |
| 29 | + <el-form-item> | |
| 30 | + <el-button type="primary" icon="el-icon-search" @click="search()">查询</el-button> | |
| 31 | + <el-button icon="el-icon-refresh-right" @click="reset()">重置</el-button> | |
| 32 | + </el-form-item> | |
| 33 | + </el-col> | |
| 34 | + </el-form> | |
| 35 | + </el-row> | |
| 36 | + <!-- 列表 --> | |
| 37 | + <NCC-table v-loading="loading" :data="list" | |
| 38 | + :header-cell-style="{ background: '#f5f7fa', color: '#606266' }"> | |
| 39 | + <el-table-column label="批次号" align="center" width="180"> | |
| 40 | + <template slot-scope="scope"> | |
| 41 | + <div class="batch-info"> | |
| 42 | + <i class="el-icon-tickets batch-icon"></i> | |
| 43 | + <span class="text-nowrap">{{ scope.row.batchNumber || '无' }}</span> | |
| 44 | + </div> | |
| 45 | + </template> | |
| 46 | + </el-table-column> | |
| 47 | + <el-table-column label="门店名称" align="center"> | |
| 48 | + <template slot-scope="scope"> | |
| 49 | + <div class="store-info"> | |
| 50 | + <i class="el-icon-office-building store-icon"></i> | |
| 51 | + <span class="text-nowrap">{{ scope.row.storeName || '无' }}</span> | |
| 52 | + </div> | |
| 53 | + </template> | |
| 54 | + </el-table-column> | |
| 55 | + <el-table-column label="产品类型" align="center"> | |
| 56 | + <template slot-scope="scope"> | |
| 57 | + <div class="product-type-info"> | |
| 58 | + <i class="el-icon-goods product-type-icon"></i> | |
| 59 | + <span class="text-nowrap">{{ scope.row.productType || '无' }}</span> | |
| 60 | + </div> | |
| 61 | + </template> | |
| 62 | + </el-table-column> | |
| 63 | + <el-table-column label="送出数量" align="center" width="120"> | |
| 64 | + <template slot-scope="scope"> | |
| 65 | + <div class="quantity-info"> | |
| 66 | + <i class="el-icon-upload2 quantity-icon"></i> | |
| 67 | + <span>{{ scope.row.sendQuantity || 0 }}</span> | |
| 68 | + </div> | |
| 69 | + </template> | |
| 70 | + </el-table-column> | |
| 71 | + <el-table-column label="送回数量" align="center" width="120"> | |
| 72 | + <template slot-scope="scope"> | |
| 73 | + <div class="quantity-info"> | |
| 74 | + <i class="el-icon-download quantity-icon"></i> | |
| 75 | + <span>{{ scope.row.returnQuantity || 0 }}</span> | |
| 76 | + </div> | |
| 77 | + </template> | |
| 78 | + </el-table-column> | |
| 79 | + <el-table-column label="差异数量" align="center" width="120"> | |
| 80 | + <template slot-scope="scope"> | |
| 81 | + <el-tag type="warning" size="small"> | |
| 82 | + <i class="el-icon-warning" style="margin-right: 4px;"></i> | |
| 83 | + {{ scope.row.differenceQuantity || 0 }} | |
| 84 | + </el-tag> | |
| 85 | + </template> | |
| 86 | + </el-table-column> | |
| 87 | + <el-table-column label="差异备注" align="center" min-width="150"> | |
| 88 | + <template slot-scope="scope"> | |
| 89 | + <div class="remark-info"> | |
| 90 | + <i class="el-icon-document remark-icon"></i> | |
| 91 | + <span class="text-nowrap">{{ scope.row.differenceRemark || '无' }}</span> | |
| 92 | + </div> | |
| 93 | + </template> | |
| 94 | + </el-table-column> | |
| 95 | + <el-table-column label="送出时间" align="center" width="180"> | |
| 96 | + <template slot-scope="scope"> | |
| 97 | + <div class="time-info"> | |
| 98 | + <i class="el-icon-time time-icon"></i> | |
| 99 | + <span class="text-nowrap">{{ formatDateTime(scope.row.sendTime) || '无' }}</span> | |
| 100 | + </div> | |
| 101 | + </template> | |
| 102 | + </el-table-column> | |
| 103 | + <el-table-column label="送回时间" align="center" width="180"> | |
| 104 | + <template slot-scope="scope"> | |
| 105 | + <div class="time-info"> | |
| 106 | + <i class="el-icon-time time-icon"></i> | |
| 107 | + <span class="text-nowrap">{{ formatDateTime(scope.row.returnTime) || '无' }}</span> | |
| 108 | + </div> | |
| 109 | + </template> | |
| 110 | + </el-table-column> | |
| 111 | + </NCC-table> | |
| 112 | + <pagination :total="total" :page.sync="query.currentPage" | |
| 113 | + :limit.sync="query.pageSize" @pagination="initData" /> | |
| 114 | + <div slot="footer" class="dialog-footer"> | |
| 115 | + <el-button @click="visible = false">关闭</el-button> | |
| 116 | + </div> | |
| 117 | + </el-dialog> | |
| 118 | +</template> | |
| 119 | + | |
| 120 | +<script> | |
| 121 | +import request from '@/utils/request' | |
| 122 | +import { getStoreSelector } from '@/api/extend/store' | |
| 123 | + | |
| 124 | +export default { | |
| 125 | + name: 'DifferenceDialog', | |
| 126 | + data() { | |
| 127 | + return { | |
| 128 | + visible: false, | |
| 129 | + loading: false, | |
| 130 | + list: [], | |
| 131 | + total: 0, | |
| 132 | + query: { | |
| 133 | + currentPage: 1, | |
| 134 | + pageSize: 20, | |
| 135 | + storeId: undefined, | |
| 136 | + productType: undefined, | |
| 137 | + startTime: undefined, | |
| 138 | + endTime: undefined | |
| 139 | + }, | |
| 140 | + storeList: [] | |
| 141 | + } | |
| 142 | + }, | |
| 143 | + mounted() { | |
| 144 | + this.initStoreList() | |
| 145 | + }, | |
| 146 | + methods: { | |
| 147 | + // 初始化 | |
| 148 | + init() { | |
| 149 | + this.visible = true | |
| 150 | + this.reset() | |
| 151 | + this.initData() | |
| 152 | + }, | |
| 153 | + // 初始化门店列表 | |
| 154 | + initStoreList() { | |
| 155 | + getStoreSelector().then(res => { | |
| 156 | + if (res.code == 200 && res.data && res.data.list) { | |
| 157 | + this.storeList = res.data.list | |
| 158 | + } | |
| 159 | + }).catch(() => { | |
| 160 | + this.storeList = [] | |
| 161 | + }) | |
| 162 | + }, | |
| 163 | + // 初始化数据 | |
| 164 | + initData() { | |
| 165 | + this.loading = true | |
| 166 | + let query = { ...this.query } | |
| 167 | + // 移除空值 | |
| 168 | + Object.keys(query).forEach(key => { | |
| 169 | + if (query[key] === undefined || query[key] === null || query[key] === '') { | |
| 170 | + delete query[key] | |
| 171 | + } | |
| 172 | + }) | |
| 173 | + request({ | |
| 174 | + url: '/api/Extend/LqLaundryFlow/GetDifferenceList', | |
| 175 | + method: 'POST', | |
| 176 | + data: query | |
| 177 | + }).then(res => { | |
| 178 | + if (res.code == 200 && res.data) { | |
| 179 | + this.list = res.data.list || [] | |
| 180 | + this.total = res.data.pagination ? res.data.pagination.total : 0 | |
| 181 | + } else { | |
| 182 | + this.list = [] | |
| 183 | + this.total = 0 | |
| 184 | + } | |
| 185 | + this.loading = false | |
| 186 | + }).catch(() => { | |
| 187 | + this.loading = false | |
| 188 | + this.list = [] | |
| 189 | + this.total = 0 | |
| 190 | + }) | |
| 191 | + }, | |
| 192 | + // 查询 | |
| 193 | + search() { | |
| 194 | + this.query.currentPage = 1 | |
| 195 | + this.initData() | |
| 196 | + }, | |
| 197 | + // 重置 | |
| 198 | + reset() { | |
| 199 | + this.query = { | |
| 200 | + currentPage: 1, | |
| 201 | + pageSize: 20, | |
| 202 | + storeId: undefined, | |
| 203 | + productType: undefined, | |
| 204 | + startTime: undefined, | |
| 205 | + endTime: undefined | |
| 206 | + } | |
| 207 | + }, | |
| 208 | + // 格式化日期时间 | |
| 209 | + formatDateTime(dateTime) { | |
| 210 | + if (!dateTime) return '' | |
| 211 | + const date = new Date(dateTime) | |
| 212 | + const year = date.getFullYear() | |
| 213 | + const month = String(date.getMonth() + 1).padStart(2, '0') | |
| 214 | + const day = String(date.getDate()).padStart(2, '0') | |
| 215 | + const hours = String(date.getHours()).padStart(2, '0') | |
| 216 | + const minutes = String(date.getMinutes()).padStart(2, '0') | |
| 217 | + const seconds = String(date.getSeconds()).padStart(2, '0') | |
| 218 | + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}` | |
| 219 | + } | |
| 220 | + } | |
| 221 | +} | |
| 222 | +</script> | |
| 223 | + | |
| 224 | +<style lang="scss" scoped> | |
| 225 | +.search-box { | |
| 226 | + margin-bottom: 16px; | |
| 227 | + padding: 16px; | |
| 228 | + background: #f5f7fa; | |
| 229 | + border-radius: 4px; | |
| 230 | + | |
| 231 | + ::v-deep .el-form-item { | |
| 232 | + margin-bottom: 0; | |
| 233 | + } | |
| 234 | + | |
| 235 | + ::v-deep .el-button { | |
| 236 | + cursor: pointer; | |
| 237 | + } | |
| 238 | +} | |
| 239 | + | |
| 240 | +// 通用文本不换行样式 | |
| 241 | +.text-nowrap { | |
| 242 | + white-space: nowrap; | |
| 243 | + overflow: hidden; | |
| 244 | + text-overflow: ellipsis; | |
| 245 | + max-width: 100%; | |
| 246 | +} | |
| 247 | + | |
| 248 | +// 信息显示样式 | |
| 249 | +.batch-info, | |
| 250 | +.store-info, | |
| 251 | +.product-type-info, | |
| 252 | +.quantity-info, | |
| 253 | +.remark-info, | |
| 254 | +.time-info { | |
| 255 | + display: flex; | |
| 256 | + align-items: center; | |
| 257 | + justify-content: center; | |
| 258 | + gap: 6px; | |
| 259 | +} | |
| 260 | + | |
| 261 | +.batch-icon { | |
| 262 | + color: #409EFF; | |
| 263 | + font-size: 16px; | |
| 264 | +} | |
| 265 | + | |
| 266 | +.store-icon { | |
| 267 | + color: #409EFF; | |
| 268 | + font-size: 16px; | |
| 269 | +} | |
| 270 | + | |
| 271 | +.product-type-icon { | |
| 272 | + color: #67C23A; | |
| 273 | + font-size: 16px; | |
| 274 | +} | |
| 275 | + | |
| 276 | +.quantity-icon { | |
| 277 | + color: #E6A23C; | |
| 278 | + font-size: 16px; | |
| 279 | +} | |
| 280 | + | |
| 281 | +.remark-icon { | |
| 282 | + color: #909399; | |
| 283 | + font-size: 16px; | |
| 284 | +} | |
| 285 | + | |
| 286 | +.time-icon { | |
| 287 | + color: #909399; | |
| 288 | + font-size: 16px; | |
| 289 | +} | |
| 290 | + | |
| 291 | +.dialog-footer { | |
| 292 | + text-align: right; | |
| 293 | +} | |
| 294 | +</style> | |
| 295 | + | ... | ... |
antis-ncc-admin/src/views/LqLaundryFlow/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-select v-model="query.flowType" placeholder="请选择流水类型" clearable> | |
| 10 | + <el-option label="送出" :value="0" /> | |
| 11 | + <el-option label="送回" :value="1" /> | |
| 12 | + </el-select> | |
| 13 | + </el-form-item> | |
| 14 | + </el-col> | |
| 15 | + <el-col :span="6"> | |
| 16 | + <el-form-item label="批次号"> | |
| 17 | + <el-input v-model="query.batchNumber" placeholder="请输入批次号" clearable /> | |
| 18 | + </el-form-item> | |
| 19 | + </el-col> | |
| 20 | + <el-col :span="6"> | |
| 21 | + <el-form-item label="门店"> | |
| 22 | + <el-select v-model="query.storeId" placeholder="请选择门店" clearable filterable> | |
| 23 | + <el-option v-for="item in storeList" :key="item.id" :label="item.fullName" :value="item.id" /> | |
| 24 | + </el-select> | |
| 25 | + </el-form-item> | |
| 26 | + </el-col> | |
| 27 | + <el-col :span="6"> | |
| 28 | + <el-form-item label="产品类型"> | |
| 29 | + <el-input v-model="query.productType" placeholder="请输入产品类型" clearable /> | |
| 30 | + </el-form-item> | |
| 31 | + </el-col> | |
| 32 | + <el-col :span="6"> | |
| 33 | + <el-form-item label="清洗商"> | |
| 34 | + <el-select v-model="query.laundrySupplierId" placeholder="请选择清洗商" clearable filterable> | |
| 35 | + <el-option v-for="item in supplierList" :key="item.id" :label="item.supplierName" :value="item.id" /> | |
| 36 | + </el-select> | |
| 37 | + </el-form-item> | |
| 38 | + </el-col> | |
| 39 | + <el-col :span="6"> | |
| 40 | + <el-form-item label="开始时间"> | |
| 41 | + <el-date-picker v-model="query.startTime" type="datetime" placeholder="选择开始时间" value-format="yyyy-MM-dd HH:mm:ss" style="width: 100%" /> | |
| 42 | + </el-form-item> | |
| 43 | + </el-col> | |
| 44 | + <el-col :span="6"> | |
| 45 | + <el-form-item label="结束时间"> | |
| 46 | + <el-date-picker v-model="query.endTime" type="datetime" placeholder="选择结束时间" value-format="yyyy-MM-dd HH:mm:ss" style="width: 100%" /> | |
| 47 | + </el-form-item> | |
| 48 | + </el-col> | |
| 49 | + <el-col :span="6"> | |
| 50 | + <el-form-item label="是否有效"> | |
| 51 | + <el-select v-model="query.isEffective" placeholder="是否有效" clearable> | |
| 52 | + <el-option label="有效" :value="1" /> | |
| 53 | + <el-option label="无效" :value="0" /> | |
| 54 | + </el-select> | |
| 55 | + </el-form-item> | |
| 56 | + </el-col> | |
| 57 | + <el-col :span="6"> | |
| 58 | + <el-form-item> | |
| 59 | + <el-button type="primary" icon="el-icon-search" @click="search()">查询</el-button> | |
| 60 | + <el-button icon="el-icon-refresh-right" @click="reset()">重置</el-button> | |
| 61 | + </el-form-item> | |
| 62 | + </el-col> | |
| 63 | + </el-form> | |
| 64 | + </el-row> | |
| 65 | + <!-- 列表 --> | |
| 66 | + <div class="NCC-common-layout-main NCC-flex-main"> | |
| 67 | + <div class="NCC-common-head"> | |
| 68 | + <div> | |
| 69 | + <el-button type="primary" icon="el-icon-plus" @click="addSend()">创建送出记录</el-button> | |
| 70 | + <el-button type="success" icon="el-icon-refresh" @click="addReturn()">创建送回记录</el-button> | |
| 71 | + <el-button type="warning" icon="el-icon-warning" @click="viewDifference()">查看差异记录</el-button> | |
| 72 | + </div> | |
| 73 | + <div class="NCC-common-head-right"> | |
| 74 | + <el-tooltip effect="dark" content="刷新" placement="top"> | |
| 75 | + <el-link icon="icon-ym icon-ym-Refresh NCC-common-head-icon" :underline="false" | |
| 76 | + @click="initData()" /> | |
| 77 | + </el-tooltip> | |
| 78 | + <screenfull isContainer /> | |
| 79 | + </div> | |
| 80 | + </div> | |
| 81 | + <NCC-table v-loading="loading" :data="list" | |
| 82 | + :header-cell-style="{ background: '#f5f7fa', color: '#606266' }"> | |
| 83 | + <el-table-column label="流水类型" align="center" width="100"> | |
| 84 | + <template slot-scope="scope"> | |
| 85 | + <el-tag :type="scope.row.flowType === 0 ? 'primary' : 'success'" size="small"> | |
| 86 | + <i :class="scope.row.flowType === 0 ? 'el-icon-upload2' : 'el-icon-download'" style="margin-right: 4px;"></i> | |
| 87 | + {{ scope.row.flowTypeName || '无' }} | |
| 88 | + </el-tag> | |
| 89 | + </template> | |
| 90 | + </el-table-column> | |
| 91 | + <el-table-column label="批次号" align="center" width="180"> | |
| 92 | + <template slot-scope="scope"> | |
| 93 | + <div class="batch-info"> | |
| 94 | + <i class="el-icon-tickets batch-icon"></i> | |
| 95 | + <span class="text-nowrap">{{ scope.row.batchNumber || '无' }}</span> | |
| 96 | + </div> | |
| 97 | + </template> | |
| 98 | + </el-table-column> | |
| 99 | + <el-table-column label="门店名称" align="center"> | |
| 100 | + <template slot-scope="scope"> | |
| 101 | + <div class="store-info"> | |
| 102 | + <i class="el-icon-office-building store-icon"></i> | |
| 103 | + <span class="text-nowrap">{{ scope.row.storeName || '无' }}</span> | |
| 104 | + </div> | |
| 105 | + </template> | |
| 106 | + </el-table-column> | |
| 107 | + <el-table-column label="产品类型" align="center"> | |
| 108 | + <template slot-scope="scope"> | |
| 109 | + <div class="product-type-info"> | |
| 110 | + <i class="el-icon-goods product-type-icon"></i> | |
| 111 | + <span class="text-nowrap">{{ scope.row.productType || '无' }}</span> | |
| 112 | + </div> | |
| 113 | + </template> | |
| 114 | + </el-table-column> | |
| 115 | + <el-table-column label="清洗商名称" align="center"> | |
| 116 | + <template slot-scope="scope"> | |
| 117 | + <div class="supplier-info"> | |
| 118 | + <i class="el-icon-s-shop supplier-icon"></i> | |
| 119 | + <span class="text-nowrap">{{ scope.row.laundrySupplierName || '无' }}</span> | |
| 120 | + </div> | |
| 121 | + </template> | |
| 122 | + </el-table-column> | |
| 123 | + <el-table-column label="数量" align="center" width="100"> | |
| 124 | + <template slot-scope="scope"> | |
| 125 | + <div class="quantity-info"> | |
| 126 | + <i class="el-icon-s-data quantity-icon"></i> | |
| 127 | + <span>{{ scope.row.quantity || 0 }}</span> | |
| 128 | + </div> | |
| 129 | + </template> | |
| 130 | + </el-table-column> | |
| 131 | + <el-table-column label="清洗单价" align="center" width="120"> | |
| 132 | + <template slot-scope="scope"> | |
| 133 | + <div class="price-info"> | |
| 134 | + <i class="el-icon-coin price-icon"></i> | |
| 135 | + <span>{{ scope.row.laundryPrice || 0 }}</span> | |
| 136 | + </div> | |
| 137 | + </template> | |
| 138 | + </el-table-column> | |
| 139 | + <el-table-column label="总费用" align="center" width="120"> | |
| 140 | + <template slot-scope="scope"> | |
| 141 | + <div class="total-price-info"> | |
| 142 | + <i class="el-icon-money total-price-icon"></i> | |
| 143 | + <span>{{ scope.row.totalPrice || 0 }}</span> | |
| 144 | + </div> | |
| 145 | + </template> | |
| 146 | + </el-table-column> | |
| 147 | + <el-table-column label="备注" align="center" min-width="150"> | |
| 148 | + <template slot-scope="scope"> | |
| 149 | + <div class="remark-info"> | |
| 150 | + <i class="el-icon-document remark-icon"></i> | |
| 151 | + <span class="text-nowrap">{{ scope.row.remark || '无' }}</span> | |
| 152 | + </div> | |
| 153 | + </template> | |
| 154 | + </el-table-column> | |
| 155 | + <el-table-column label="是否有效" align="center" width="100"> | |
| 156 | + <template slot-scope="scope"> | |
| 157 | + <el-tag :type="scope.row.isEffective === 1 ? 'success' : 'info'" size="small"> | |
| 158 | + {{ scope.row.isEffective === 1 ? '有效' : '无效' }} | |
| 159 | + </el-tag> | |
| 160 | + </template> | |
| 161 | + </el-table-column> | |
| 162 | + <el-table-column label="创建人" align="center"> | |
| 163 | + <template slot-scope="scope"> | |
| 164 | + <div class="user-info"> | |
| 165 | + <i class="el-icon-user user-icon"></i> | |
| 166 | + <span class="text-nowrap">{{ scope.row.createUserName || '无' }}</span> | |
| 167 | + </div> | |
| 168 | + </template> | |
| 169 | + </el-table-column> | |
| 170 | + <el-table-column label="创建时间" align="center" width="180" prop="createTime"> | |
| 171 | + <template slot-scope="scope"> | |
| 172 | + <div class="time-info"> | |
| 173 | + <i class="el-icon-time time-icon"></i> | |
| 174 | + <span class="text-nowrap">{{ formatDateTime(scope.row.createTime) || '无' }}</span> | |
| 175 | + </div> | |
| 176 | + </template> | |
| 177 | + </el-table-column> | |
| 178 | + <el-table-column label="操作" width="150" align="left" fixed="right"> | |
| 179 | + <template slot-scope="scope"> | |
| 180 | + <div class="action-buttons"> | |
| 181 | + <el-button type="text" icon="el-icon-view" @click="viewDetail(scope.row)" class="view-btn"> | |
| 182 | + 详情 | |
| 183 | + </el-button> | |
| 184 | + <!-- <el-button v-if="scope.row.flowTypeName == '送出'" type="text" icon="el-icon-edit" class="edit-btn"> | |
| 185 | + 创建送回 | |
| 186 | + </el-button> --> | |
| 187 | + </div> | |
| 188 | + </template> | |
| 189 | + </el-table-column> | |
| 190 | + </NCC-table> | |
| 191 | + <pagination :total="total" :page.sync="query.currentPage" | |
| 192 | + :limit.sync="query.pageSize" @pagination="initData" /> | |
| 193 | + </div> | |
| 194 | + </div> | |
| 195 | + <!-- 送出记录弹窗 --> | |
| 196 | + <SendDialog v-if="sendDialogVisible" ref="SendDialog" @refresh="refresh" /> | |
| 197 | + <!-- 送回记录弹窗 --> | |
| 198 | + <ReturnDialog v-if="returnDialogVisible" ref="ReturnDialog" @refresh="refresh" /> | |
| 199 | + <!-- 详情弹窗 --> | |
| 200 | + <DetailDialog v-if="detailDialogVisible" ref="DetailDialog" /> | |
| 201 | + <!-- 差异记录弹窗 --> | |
| 202 | + <DifferenceDialog v-if="differenceDialogVisible" ref="DifferenceDialog" /> | |
| 203 | + </div> | |
| 204 | +</template> | |
| 205 | + | |
| 206 | +<script> | |
| 207 | +import request from '@/utils/request' | |
| 208 | +import { getStoreSelector } from '@/api/extend/store' | |
| 209 | +import SendDialog from './send-dialog.vue' | |
| 210 | +import ReturnDialog from './return-dialog.vue' | |
| 211 | +import DetailDialog from './detail-dialog.vue' | |
| 212 | +import DifferenceDialog from './difference-dialog.vue' | |
| 213 | + | |
| 214 | +export default { | |
| 215 | + components: { SendDialog, ReturnDialog, DetailDialog, DifferenceDialog }, | |
| 216 | + data() { | |
| 217 | + return { | |
| 218 | + list: [], | |
| 219 | + loading: false, | |
| 220 | + total: 0, | |
| 221 | + query: { | |
| 222 | + currentPage: 1, | |
| 223 | + pageSize: 20, | |
| 224 | + flowType: undefined, | |
| 225 | + batchNumber: undefined, | |
| 226 | + storeId: undefined, | |
| 227 | + productType: undefined, | |
| 228 | + laundrySupplierId: undefined, | |
| 229 | + startTime: undefined, | |
| 230 | + endTime: undefined, | |
| 231 | + isEffective: undefined | |
| 232 | + }, | |
| 233 | + storeList: [], | |
| 234 | + supplierList: [], | |
| 235 | + sendDialogVisible: false, | |
| 236 | + returnDialogVisible: false, | |
| 237 | + detailDialogVisible: false, | |
| 238 | + differenceDialogVisible: false | |
| 239 | + } | |
| 240 | + }, | |
| 241 | + created() { | |
| 242 | + this.initStoreList() | |
| 243 | + this.initSupplierList() | |
| 244 | + this.initData() | |
| 245 | + }, | |
| 246 | + methods: { | |
| 247 | + // 初始化门店列表 | |
| 248 | + initStoreList() { | |
| 249 | + getStoreSelector().then(res => { | |
| 250 | + if (res.code == 200 && res.data && res.data.list) { | |
| 251 | + this.storeList = res.data.list | |
| 252 | + } | |
| 253 | + }).catch(() => { | |
| 254 | + this.storeList = [] | |
| 255 | + }) | |
| 256 | + }, | |
| 257 | + // 初始化清洗商列表 | |
| 258 | + initSupplierList() { | |
| 259 | + request({ | |
| 260 | + url: '/api/Extend/LqLaundrySupplier/GetList', | |
| 261 | + method: 'GET', | |
| 262 | + data: { currentPage: 1, pageSize: 1000, isEffective: 1 } | |
| 263 | + }).then(res => { | |
| 264 | + if (res.code == 200 && res.data && res.data.list) { | |
| 265 | + this.supplierList = res.data.list | |
| 266 | + } | |
| 267 | + }).catch(() => { | |
| 268 | + this.supplierList = [] | |
| 269 | + }) | |
| 270 | + }, | |
| 271 | + // 初始化数据 | |
| 272 | + initData() { | |
| 273 | + this.loading = true | |
| 274 | + let query = { ...this.query } | |
| 275 | + // 移除空值 | |
| 276 | + Object.keys(query).forEach(key => { | |
| 277 | + if (query[key] === undefined || query[key] === null || query[key] === '') { | |
| 278 | + delete query[key] | |
| 279 | + } | |
| 280 | + }) | |
| 281 | + request({ | |
| 282 | + url: '/api/Extend/LqLaundryFlow/GetList', | |
| 283 | + method: 'GET', | |
| 284 | + data: query | |
| 285 | + }).then(res => { | |
| 286 | + if (res.code == 200 && res.data) { | |
| 287 | + this.list = res.data.list || [] | |
| 288 | + this.total = res.data.pagination ? res.data.pagination.total : 0 | |
| 289 | + } else { | |
| 290 | + this.list = [] | |
| 291 | + this.total = 0 | |
| 292 | + } | |
| 293 | + this.loading = false | |
| 294 | + }).catch(() => { | |
| 295 | + this.loading = false | |
| 296 | + this.list = [] | |
| 297 | + this.total = 0 | |
| 298 | + }) | |
| 299 | + }, | |
| 300 | + // 查询 | |
| 301 | + search() { | |
| 302 | + this.query.currentPage = 1 | |
| 303 | + this.initData() | |
| 304 | + }, | |
| 305 | + // 重置 | |
| 306 | + reset() { | |
| 307 | + this.query = { | |
| 308 | + currentPage: 1, | |
| 309 | + pageSize: 20, | |
| 310 | + flowType: undefined, | |
| 311 | + batchNumber: undefined, | |
| 312 | + storeId: undefined, | |
| 313 | + productType: undefined, | |
| 314 | + laundrySupplierId: undefined, | |
| 315 | + startTime: undefined, | |
| 316 | + endTime: undefined, | |
| 317 | + isEffective: undefined | |
| 318 | + } | |
| 319 | + this.initData() | |
| 320 | + }, | |
| 321 | + // 创建送出记录 | |
| 322 | + addSend() { | |
| 323 | + this.sendDialogVisible = true | |
| 324 | + this.$nextTick(() => { | |
| 325 | + this.$refs.SendDialog.init() | |
| 326 | + }) | |
| 327 | + }, | |
| 328 | + // 创建送回记录 | |
| 329 | + addReturn() { | |
| 330 | + this.returnDialogVisible = true | |
| 331 | + this.$nextTick(() => { | |
| 332 | + this.$refs.ReturnDialog.init() | |
| 333 | + }) | |
| 334 | + }, | |
| 335 | + // 查看详情 | |
| 336 | + viewDetail(row) { | |
| 337 | + this.detailDialogVisible = true | |
| 338 | + this.$nextTick(() => { | |
| 339 | + this.$refs.DetailDialog.init(row.id) | |
| 340 | + }) | |
| 341 | + }, | |
| 342 | + // 查看差异记录 | |
| 343 | + viewDifference() { | |
| 344 | + this.differenceDialogVisible = true | |
| 345 | + this.$nextTick(() => { | |
| 346 | + this.$refs.DifferenceDialog.init() | |
| 347 | + }) | |
| 348 | + }, | |
| 349 | + // 刷新 | |
| 350 | + refresh() { | |
| 351 | + this.sendDialogVisible = false | |
| 352 | + this.returnDialogVisible = false | |
| 353 | + this.initData() | |
| 354 | + }, | |
| 355 | + // 格式化日期时间 | |
| 356 | + formatDateTime(dateTime) { | |
| 357 | + if (!dateTime) return '' | |
| 358 | + const date = new Date(dateTime) | |
| 359 | + const year = date.getFullYear() | |
| 360 | + const month = String(date.getMonth() + 1).padStart(2, '0') | |
| 361 | + const day = String(date.getDate()).padStart(2, '0') | |
| 362 | + const hours = String(date.getHours()).padStart(2, '0') | |
| 363 | + const minutes = String(date.getMinutes()).padStart(2, '0') | |
| 364 | + const seconds = String(date.getSeconds()).padStart(2, '0') | |
| 365 | + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}` | |
| 366 | + } | |
| 367 | + } | |
| 368 | +} | |
| 369 | +</script> | |
| 370 | + | |
| 371 | +<style lang="scss" scoped> | |
| 372 | +// 通用文本不换行样式 | |
| 373 | +.text-nowrap { | |
| 374 | + white-space: nowrap; | |
| 375 | + overflow: hidden; | |
| 376 | + text-overflow: ellipsis; | |
| 377 | + max-width: 100%; | |
| 378 | +} | |
| 379 | + | |
| 380 | +// 信息显示样式 | |
| 381 | +.batch-info, | |
| 382 | +.store-info, | |
| 383 | +.product-type-info, | |
| 384 | +.supplier-info, | |
| 385 | +.quantity-info, | |
| 386 | +.price-info, | |
| 387 | +.total-price-info, | |
| 388 | +.remark-info, | |
| 389 | +.user-info, | |
| 390 | +.time-info { | |
| 391 | + display: flex; | |
| 392 | + align-items: center; | |
| 393 | + justify-content: center; | |
| 394 | + gap: 6px; | |
| 395 | +} | |
| 396 | + | |
| 397 | +.batch-icon { | |
| 398 | + color: #409EFF; | |
| 399 | + font-size: 16px; | |
| 400 | +} | |
| 401 | + | |
| 402 | +.store-icon { | |
| 403 | + color: #409EFF; | |
| 404 | + font-size: 16px; | |
| 405 | +} | |
| 406 | + | |
| 407 | +.product-type-icon { | |
| 408 | + color: #67C23A; | |
| 409 | + font-size: 16px; | |
| 410 | +} | |
| 411 | + | |
| 412 | +.supplier-icon { | |
| 413 | + color: #409EFF; | |
| 414 | + font-size: 16px; | |
| 415 | +} | |
| 416 | + | |
| 417 | +.quantity-icon { | |
| 418 | + color: #E6A23C; | |
| 419 | + font-size: 16px; | |
| 420 | +} | |
| 421 | + | |
| 422 | +.price-icon { | |
| 423 | + color: #E6A23C; | |
| 424 | + font-size: 16px; | |
| 425 | +} | |
| 426 | + | |
| 427 | +.total-price-icon { | |
| 428 | + color: #F56C6C; | |
| 429 | + font-size: 16px; | |
| 430 | +} | |
| 431 | + | |
| 432 | +.remark-icon { | |
| 433 | + color: #909399; | |
| 434 | + font-size: 16px; | |
| 435 | +} | |
| 436 | + | |
| 437 | +.user-icon { | |
| 438 | + color: #909399; | |
| 439 | + font-size: 16px; | |
| 440 | +} | |
| 441 | + | |
| 442 | +.time-icon { | |
| 443 | + color: #909399; | |
| 444 | + font-size: 16px; | |
| 445 | +} | |
| 446 | + | |
| 447 | +// 操作按钮样式 | |
| 448 | +.action-buttons { | |
| 449 | + display: flex; | |
| 450 | + align-items: center; | |
| 451 | + gap: 8px; | |
| 452 | +} | |
| 453 | + | |
| 454 | +.view-btn { | |
| 455 | + color: #409EFF; | |
| 456 | + | |
| 457 | + &:hover { | |
| 458 | + color: #66b1ff; | |
| 459 | + } | |
| 460 | +} | |
| 461 | + | |
| 462 | +// 表格行悬停效果 | |
| 463 | +::v-deep .el-table__row:hover { | |
| 464 | + background-color: #f5f7fa; | |
| 465 | +} | |
| 466 | + | |
| 467 | +// 表格头部样式 | |
| 468 | +::v-deep .el-table__header-wrapper { | |
| 469 | + .el-table__header { | |
| 470 | + th { | |
| 471 | + background-color: #f5f7fa; | |
| 472 | + color: #606266; | |
| 473 | + font-weight: 600; | |
| 474 | + } | |
| 475 | + } | |
| 476 | +} | |
| 477 | +</style> | |
| 478 | + | ... | ... |
antis-ncc-admin/src/views/LqLaundryFlow/return-dialog.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog title="创建送回记录" :visible.sync="visible" width="600px" :close-on-click-modal="false"> | |
| 3 | + <el-form ref="form" :model="form" :rules="rules" label-width="120px"> | |
| 4 | + <el-form-item label="批次号" prop="batchNumber"> | |
| 5 | + <el-select v-model="form.batchNumber" placeholder="请选择批次号" filterable style="width: 100%" @change="handleBatchChange"> | |
| 6 | + <el-option v-for="item in batchList" :key="item.batchNumber" :label="`${item.batchNumber} (${item.storeName} - ${item.productType} - 送出${item.quantity}件)`" :value="item.batchNumber" /> | |
| 7 | + </el-select> | |
| 8 | + </el-form-item> | |
| 9 | + <el-form-item label="门店"> | |
| 10 | + <el-input v-model="sendRecordInfo.storeName" disabled /> | |
| 11 | + </el-form-item> | |
| 12 | + <el-form-item label="产品类型"> | |
| 13 | + <el-input v-model="sendRecordInfo.productType" disabled /> | |
| 14 | + </el-form-item> | |
| 15 | + <el-form-item label="送出数量"> | |
| 16 | + <el-input v-model="sendRecordInfo.quantity" disabled /> | |
| 17 | + </el-form-item> | |
| 18 | + <el-form-item label="清洗商" prop="laundrySupplierId"> | |
| 19 | + <el-select v-model="form.laundrySupplierId" placeholder="请选择清洗商" filterable style="width: 100%" @change="handleSupplierChange"> | |
| 20 | + <el-option v-for="item in filteredSupplierList" :key="item.id" :label="item.supplierName" :value="item.id" /> | |
| 21 | + </el-select> | |
| 22 | + </el-form-item> | |
| 23 | + <el-form-item label="清洗单价" prop="laundryPrice"> | |
| 24 | + <el-input v-model="form.laundryPrice" placeholder="自动填充" disabled /> | |
| 25 | + </el-form-item> | |
| 26 | + <el-form-item label="送回数量" prop="quantity"> | |
| 27 | + <el-input-number v-model="form.quantity" :min="0" :precision="0" style="width: 100%" /> | |
| 28 | + </el-form-item> | |
| 29 | + <el-form-item label="预计总费用"> | |
| 30 | + <el-input v-model="totalPrice" disabled /> | |
| 31 | + </el-form-item> | |
| 32 | + <el-form-item label="备注"> | |
| 33 | + <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注(可说明差异原因,如损坏、丢失等)" /> | |
| 34 | + </el-form-item> | |
| 35 | + </el-form> | |
| 36 | + <div slot="footer" class="dialog-footer"> | |
| 37 | + <el-button @click="visible = false">取消</el-button> | |
| 38 | + <el-button type="primary" @click="submit" :loading="loading">确定</el-button> | |
| 39 | + </div> | |
| 40 | + </el-dialog> | |
| 41 | +</template> | |
| 42 | + | |
| 43 | +<script> | |
| 44 | +import request from '@/utils/request' | |
| 45 | + | |
| 46 | +export default { | |
| 47 | + name: 'ReturnDialog', | |
| 48 | + data() { | |
| 49 | + return { | |
| 50 | + visible: false, | |
| 51 | + loading: false, | |
| 52 | + form: { | |
| 53 | + batchNumber: '', | |
| 54 | + laundrySupplierId: '', | |
| 55 | + quantity: 0, | |
| 56 | + remark: '' | |
| 57 | + }, | |
| 58 | + rules: { | |
| 59 | + batchNumber: [{ required: true, message: '请选择批次号', trigger: 'change' }], | |
| 60 | + laundrySupplierId: [{ required: true, message: '请选择清洗商', trigger: 'blur' }], | |
| 61 | + quantity: [{ required: true, message: '请输入送回数量', trigger: 'blur' }] | |
| 62 | + }, | |
| 63 | + batchList: [], | |
| 64 | + supplierList: [], | |
| 65 | + allSupplierList: [], // 保存所有清洗商列表 | |
| 66 | + sendRecordInfo: { | |
| 67 | + storeName: '', | |
| 68 | + productType: '', | |
| 69 | + quantity: 0 | |
| 70 | + }, | |
| 71 | + selectedSupplier: null | |
| 72 | + } | |
| 73 | + }, | |
| 74 | + computed: { | |
| 75 | + totalPrice() { | |
| 76 | + if (this.form.quantity && this.form.laundryPrice) { | |
| 77 | + return (this.form.quantity * this.form.laundryPrice).toFixed(2) | |
| 78 | + } | |
| 79 | + return '0.00' | |
| 80 | + }, | |
| 81 | + // 过滤后的清洗商列表(根据产品类型) | |
| 82 | + filteredSupplierList() { | |
| 83 | + if (!this.sendRecordInfo.productType) { | |
| 84 | + return this.allSupplierList | |
| 85 | + } | |
| 86 | + return this.allSupplierList.filter(supplier => supplier.productType === this.sendRecordInfo.productType) | |
| 87 | + } | |
| 88 | + }, | |
| 89 | + mounted() { | |
| 90 | + this.initSupplierList() | |
| 91 | + this.initBatchList() | |
| 92 | + }, | |
| 93 | + methods: { | |
| 94 | + // 初始化 | |
| 95 | + init() { | |
| 96 | + this.visible = true | |
| 97 | + this.form = { | |
| 98 | + batchNumber: '', | |
| 99 | + laundrySupplierId: '', | |
| 100 | + quantity: 0, | |
| 101 | + remark: '' | |
| 102 | + } | |
| 103 | + this.sendRecordInfo = { | |
| 104 | + storeName: '', | |
| 105 | + productType: '', | |
| 106 | + quantity: 0 | |
| 107 | + } | |
| 108 | + this.selectedSupplier = null | |
| 109 | + this.initBatchList() | |
| 110 | + this.$nextTick(() => { | |
| 111 | + if (this.$refs.form) { | |
| 112 | + this.$refs.form.clearValidate() | |
| 113 | + } | |
| 114 | + }) | |
| 115 | + }, | |
| 116 | + // 初始化批次列表(只显示已送出但未送回或送回数量小于送出数量的记录) | |
| 117 | + initBatchList() { | |
| 118 | + request({ | |
| 119 | + url: '/api/Extend/LqLaundryFlow/GetList', | |
| 120 | + method: 'GET', | |
| 121 | + data: { currentPage: 1, pageSize: 1000, flowType: 0, isEffective: 1 } | |
| 122 | + }).then(res => { | |
| 123 | + if (res.code == 200 && res.data && res.data.list) { | |
| 124 | + // 获取所有送出记录 | |
| 125 | + const sendRecords = res.data.list | |
| 126 | + // 获取所有送回记录 | |
| 127 | + request({ | |
| 128 | + url: '/api/Extend/LqLaundryFlow/GetList', | |
| 129 | + method: 'GET', | |
| 130 | + data: { currentPage: 1, pageSize: 1000, flowType: 1, isEffective: 1 } | |
| 131 | + }).then(returnRes => { | |
| 132 | + if (returnRes.code == 200 && returnRes.data && returnRes.data.list) { | |
| 133 | + const returnRecords = returnRes.data.list | |
| 134 | + // 按批次号分组统计送回数量 | |
| 135 | + const returnMap = {} | |
| 136 | + returnRecords.forEach(item => { | |
| 137 | + if (!returnMap[item.batchNumber]) { | |
| 138 | + returnMap[item.batchNumber] = 0 | |
| 139 | + } | |
| 140 | + returnMap[item.batchNumber] += item.quantity | |
| 141 | + }) | |
| 142 | + // 过滤出可以送回记录的批次(送出数量 > 已送回数量) | |
| 143 | + this.batchList = sendRecords.filter(send => { | |
| 144 | + const returnedQty = returnMap[send.batchNumber] || 0 | |
| 145 | + return send.quantity > returnedQty | |
| 146 | + }).map(send => ({ | |
| 147 | + batchNumber: send.batchNumber, | |
| 148 | + storeName: send.storeName, | |
| 149 | + productType: send.productType, | |
| 150 | + quantity: send.quantity, | |
| 151 | + returnedQty: returnMap[send.batchNumber] || 0 | |
| 152 | + })) | |
| 153 | + } else { | |
| 154 | + // 如果没有送回记录,所有送出记录都可以送回 | |
| 155 | + this.batchList = sendRecords.map(send => ({ | |
| 156 | + batchNumber: send.batchNumber, | |
| 157 | + storeName: send.storeName, | |
| 158 | + productType: send.productType, | |
| 159 | + quantity: send.quantity, | |
| 160 | + returnedQty: 0 | |
| 161 | + })) | |
| 162 | + } | |
| 163 | + }).catch(() => { | |
| 164 | + // 如果获取送回记录失败,所有送出记录都可以送回 | |
| 165 | + this.batchList = sendRecords.map(send => ({ | |
| 166 | + batchNumber: send.batchNumber, | |
| 167 | + storeName: send.storeName, | |
| 168 | + productType: send.productType, | |
| 169 | + quantity: send.quantity, | |
| 170 | + returnedQty: 0 | |
| 171 | + })) | |
| 172 | + }) | |
| 173 | + } else { | |
| 174 | + this.batchList = [] | |
| 175 | + } | |
| 176 | + }).catch(() => { | |
| 177 | + this.batchList = [] | |
| 178 | + }) | |
| 179 | + }, | |
| 180 | + // 初始化清洗商列表 | |
| 181 | + initSupplierList() { | |
| 182 | + request({ | |
| 183 | + url: '/api/Extend/LqLaundrySupplier/GetList', | |
| 184 | + method: 'GET', | |
| 185 | + data: { currentPage: 1, pageSize: 1000, isEffective: 1 } | |
| 186 | + }).then(res => { | |
| 187 | + if (res.code == 200 && res.data && res.data.list) { | |
| 188 | + this.allSupplierList = res.data.list | |
| 189 | + this.supplierList = res.data.list | |
| 190 | + } | |
| 191 | + }).catch(() => { | |
| 192 | + this.allSupplierList = [] | |
| 193 | + this.supplierList = [] | |
| 194 | + }) | |
| 195 | + }, | |
| 196 | + // 批次号变化 | |
| 197 | + handleBatchChange(value) { | |
| 198 | + const batch = this.batchList.find(item => item.batchNumber === value) | |
| 199 | + if (batch) { | |
| 200 | + this.sendRecordInfo = { | |
| 201 | + storeName: batch.storeName, | |
| 202 | + productType: batch.productType, | |
| 203 | + quantity: batch.quantity | |
| 204 | + } | |
| 205 | + // 清空已选择的清洗商 | |
| 206 | + this.form.laundrySupplierId = '' | |
| 207 | + this.form.laundryPrice = 0 | |
| 208 | + this.selectedSupplier = null | |
| 209 | + } else { | |
| 210 | + this.sendRecordInfo = { | |
| 211 | + storeName: '', | |
| 212 | + productType: '', | |
| 213 | + quantity: 0 | |
| 214 | + } | |
| 215 | + } | |
| 216 | + }, | |
| 217 | + // 清洗商变化 | |
| 218 | + handleSupplierChange(value) { | |
| 219 | + const supplier = this.filteredSupplierList.find(item => item.id === value) | |
| 220 | + if (supplier) { | |
| 221 | + this.selectedSupplier = supplier | |
| 222 | + this.$set(this.form, 'laundryPrice', supplier.laundryPrice || 0) | |
| 223 | + } else { | |
| 224 | + this.selectedSupplier = null | |
| 225 | + this.$set(this.form, 'laundryPrice', 0) | |
| 226 | + } | |
| 227 | + }, | |
| 228 | + // 提交 | |
| 229 | + submit() { | |
| 230 | + this.$refs.form.validate(valid => { | |
| 231 | + if (!valid) return | |
| 232 | + | |
| 233 | + // 验证产品类型是否匹配(通过计算属性已经过滤,这里做二次验证) | |
| 234 | + if (this.selectedSupplier && this.sendRecordInfo.productType && this.sendRecordInfo.productType !== this.selectedSupplier.productType) { | |
| 235 | + this.$message.error(`清洗商【${this.selectedSupplier.supplierName}】不支持清洗产品类型【${this.sendRecordInfo.productType}】`) | |
| 236 | + return | |
| 237 | + } | |
| 238 | + | |
| 239 | + this.loading = true | |
| 240 | + request({ | |
| 241 | + url: '/api/Extend/LqLaundryFlow/Return', | |
| 242 | + method: 'POST', | |
| 243 | + data: { | |
| 244 | + batchNumber: this.form.batchNumber, | |
| 245 | + laundrySupplierId: this.form.laundrySupplierId, | |
| 246 | + quantity: this.form.quantity, | |
| 247 | + remark: this.form.remark | |
| 248 | + } | |
| 249 | + }).then(res => { | |
| 250 | + this.loading = false | |
| 251 | + if (res.code == 200) { | |
| 252 | + this.$message.success(res.msg || '创建成功') | |
| 253 | + this.visible = false | |
| 254 | + this.$emit('refresh') | |
| 255 | + } else { | |
| 256 | + this.$message.error(res.msg || '创建失败') | |
| 257 | + } | |
| 258 | + }).catch(() => { | |
| 259 | + this.loading = false | |
| 260 | + }) | |
| 261 | + }) | |
| 262 | + } | |
| 263 | + }, | |
| 264 | + watch: { | |
| 265 | + visible(val) { | |
| 266 | + if (!val) { | |
| 267 | + this.$refs.form && this.$refs.form.resetFields() | |
| 268 | + } | |
| 269 | + } | |
| 270 | + } | |
| 271 | +} | |
| 272 | +</script> | |
| 273 | + | |
| 274 | +<style lang="scss" scoped> | |
| 275 | +.dialog-footer { | |
| 276 | + text-align: right; | |
| 277 | +} | |
| 278 | +</style> | |
| 279 | + | ... | ... |
antis-ncc-admin/src/views/LqLaundryFlow/send-dialog.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog title="创建送出记录" :visible.sync="visible" width="600px" :close-on-click-modal="false"> | |
| 3 | + <el-form ref="form" :model="form" :rules="rules" label-width="120px"> | |
| 4 | + <el-form-item label="门店" prop="storeId"> | |
| 5 | + <el-select v-model="form.storeId" placeholder="请选择门店" filterable style="width: 100%" @change="handleStoreChange"> | |
| 6 | + <el-option v-for="item in storeList" :key="item.id" :label="item.fullName" :value="item.id" /> | |
| 7 | + </el-select> | |
| 8 | + </el-form-item> | |
| 9 | + <el-form-item label="产品类型" prop="productType"> | |
| 10 | + <el-select v-model="form.productType" placeholder="请选择产品类型" clearable filterable style="width: 100%"> | |
| 11 | + <el-option v-for="item in productTypeOptions" :key="item.Value" :label="item.Name" :value="item.Name" /> | |
| 12 | + </el-select> | |
| 13 | + </el-form-item> | |
| 14 | + <el-form-item label="清洗商" prop="laundrySupplierId"> | |
| 15 | + <el-select v-model="form.laundrySupplierId" placeholder="请选择清洗商" filterable style="width: 100%" @change="handleSupplierChange"> | |
| 16 | + <el-option v-for="item in supplierList" :key="item.id" :label="item.supplierName" :value="item.id" /> | |
| 17 | + </el-select> | |
| 18 | + </el-form-item> | |
| 19 | + <el-form-item label="清洗单价" prop="laundryPrice"> | |
| 20 | + <el-input v-model="form.laundryPrice" placeholder="自动填充" disabled /> | |
| 21 | + </el-form-item> | |
| 22 | + <el-form-item label="送出数量" prop="quantity"> | |
| 23 | + <el-input-number v-model="form.quantity" :min="1" :precision="0" style="width: 100%" /> | |
| 24 | + </el-form-item> | |
| 25 | + <el-form-item label="备注"> | |
| 26 | + <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" /> | |
| 27 | + </el-form-item> | |
| 28 | + </el-form> | |
| 29 | + <div slot="footer" class="dialog-footer"> | |
| 30 | + <el-button @click="visible = false">取消</el-button> | |
| 31 | + <el-button type="primary" @click="submit" :loading="loading">确定</el-button> | |
| 32 | + </div> | |
| 33 | + </el-dialog> | |
| 34 | +</template> | |
| 35 | + | |
| 36 | +<script> | |
| 37 | +import request from '@/utils/request' | |
| 38 | +import { getStoreSelector } from '@/api/extend/store' | |
| 39 | + | |
| 40 | +export default { | |
| 41 | + name: 'SendDialog', | |
| 42 | + data() { | |
| 43 | + return { | |
| 44 | + visible: false, | |
| 45 | + loading: false, | |
| 46 | + form: { | |
| 47 | + storeId: '', | |
| 48 | + productType: '', | |
| 49 | + laundrySupplierId: '', | |
| 50 | + quantity: 1, | |
| 51 | + remark: '' | |
| 52 | + }, | |
| 53 | + rules: { | |
| 54 | + storeId: [{ required: true, message: '请选择门店', trigger: 'change' }], | |
| 55 | + productType: [{ required: true, message: '请选择产品类型', trigger: 'change' }], | |
| 56 | + laundrySupplierId: [{ required: true, message: '请选择清洗商', trigger: 'change' }], | |
| 57 | + quantity: [{ required: true, message: '请输入送出数量', trigger: 'blur' }] | |
| 58 | + }, | |
| 59 | + storeList: [], | |
| 60 | + supplierList: [], | |
| 61 | + selectedSupplier: null, | |
| 62 | + productTypeOptions: [] | |
| 63 | + } | |
| 64 | + }, | |
| 65 | + mounted() { | |
| 66 | + this.initStoreList() | |
| 67 | + this.initSupplierList() | |
| 68 | + this.loadProductTypeOptions() | |
| 69 | + }, | |
| 70 | + methods: { | |
| 71 | + // 初始化 | |
| 72 | + init() { | |
| 73 | + this.visible = true | |
| 74 | + this.form = { | |
| 75 | + storeId: '', | |
| 76 | + productType: '', | |
| 77 | + laundrySupplierId: '', | |
| 78 | + quantity: 1, | |
| 79 | + remark: '' | |
| 80 | + } | |
| 81 | + this.selectedSupplier = null | |
| 82 | + this.$nextTick(() => { | |
| 83 | + if (this.$refs.form) { | |
| 84 | + this.$refs.form.clearValidate() | |
| 85 | + } | |
| 86 | + }) | |
| 87 | + }, | |
| 88 | + // 初始化门店列表 | |
| 89 | + initStoreList() { | |
| 90 | + getStoreSelector().then(res => { | |
| 91 | + if (res.code == 200 && res.data && res.data.list) { | |
| 92 | + this.storeList = res.data.list | |
| 93 | + } | |
| 94 | + }).catch(() => { | |
| 95 | + this.storeList = [] | |
| 96 | + }) | |
| 97 | + }, | |
| 98 | + // 初始化清洗商列表 | |
| 99 | + initSupplierList() { | |
| 100 | + request({ | |
| 101 | + url: '/api/Extend/LqLaundrySupplier/GetList', | |
| 102 | + method: 'GET', | |
| 103 | + data: { currentPage: 1, pageSize: 1000, isEffective: 1 } | |
| 104 | + }).then(res => { | |
| 105 | + if (res.code == 200 && res.data && res.data.list) { | |
| 106 | + this.supplierList = res.data.list | |
| 107 | + } | |
| 108 | + }).catch(() => { | |
| 109 | + this.supplierList = [] | |
| 110 | + }) | |
| 111 | + }, | |
| 112 | + // 加载产品类型选项 | |
| 113 | + loadProductTypeOptions() { | |
| 114 | + request({ | |
| 115 | + url: '/api/Extend/LqStoreConsumableInventory/consumable-product-type', | |
| 116 | + method: 'GET' | |
| 117 | + }).then(res => { | |
| 118 | + if (res.code == 200 && res.data && Array.isArray(res.data)) { | |
| 119 | + this.productTypeOptions = res.data | |
| 120 | + } else { | |
| 121 | + this.productTypeOptions = [] | |
| 122 | + } | |
| 123 | + }).catch(() => { | |
| 124 | + this.productTypeOptions = [] | |
| 125 | + }) | |
| 126 | + }, | |
| 127 | + // 门店变化 | |
| 128 | + handleStoreChange() { | |
| 129 | + // 可以在这里添加逻辑 | |
| 130 | + }, | |
| 131 | + // 清洗商变化 | |
| 132 | + handleSupplierChange(value) { | |
| 133 | + const supplier = this.supplierList.find(item => item.id === value) | |
| 134 | + if (supplier) { | |
| 135 | + this.selectedSupplier = supplier | |
| 136 | + this.$set(this.form, 'laundryPrice', supplier.laundryPrice || 0) | |
| 137 | + // 如果已选择产品类型,验证是否匹配 | |
| 138 | + if (this.form.productType && supplier.productType !== this.form.productType) { | |
| 139 | + this.$message.warning(`清洗商【${supplier.supplierName}】不支持清洗产品类型【${this.form.productType}】,请重新选择`) | |
| 140 | + } | |
| 141 | + } else { | |
| 142 | + this.selectedSupplier = null | |
| 143 | + this.$set(this.form, 'laundryPrice', 0) | |
| 144 | + } | |
| 145 | + }, | |
| 146 | + // 提交 | |
| 147 | + submit() { | |
| 148 | + this.$refs.form.validate(valid => { | |
| 149 | + if (!valid) return | |
| 150 | + | |
| 151 | + // 验证产品类型是否匹配 | |
| 152 | + if (this.selectedSupplier && this.form.productType !== this.selectedSupplier.productType) { | |
| 153 | + this.$message.error(`清洗商【${this.selectedSupplier.supplierName}】不支持清洗产品类型【${this.form.productType}】`) | |
| 154 | + return | |
| 155 | + } | |
| 156 | + | |
| 157 | + this.loading = true | |
| 158 | + request({ | |
| 159 | + url: '/api/Extend/LqLaundryFlow/Send', | |
| 160 | + method: 'POST', | |
| 161 | + data: { | |
| 162 | + storeId: this.form.storeId, | |
| 163 | + productType: this.form.productType, | |
| 164 | + laundrySupplierId: this.form.laundrySupplierId, | |
| 165 | + quantity: this.form.quantity, | |
| 166 | + remark: this.form.remark | |
| 167 | + } | |
| 168 | + }).then(res => { | |
| 169 | + this.loading = false | |
| 170 | + if (res.code == 200) { | |
| 171 | + this.$message.success(res.msg || '创建成功') | |
| 172 | + this.visible = false | |
| 173 | + this.$emit('refresh') | |
| 174 | + } else { | |
| 175 | + this.$message.error(res.msg || '创建失败') | |
| 176 | + } | |
| 177 | + }).catch(() => { | |
| 178 | + this.loading = false | |
| 179 | + }) | |
| 180 | + }) | |
| 181 | + } | |
| 182 | + }, | |
| 183 | + watch: { | |
| 184 | + visible(val) { | |
| 185 | + if (!val) { | |
| 186 | + this.$refs.form && this.$refs.form.resetFields() | |
| 187 | + } | |
| 188 | + } | |
| 189 | + } | |
| 190 | +} | |
| 191 | +</script> | |
| 192 | + | |
| 193 | +<style lang="scss" scoped> | |
| 194 | +.dialog-footer { | |
| 195 | + text-align: right; | |
| 196 | +} | |
| 197 | +</style> | |
| 198 | + | ... | ... |
antis-ncc-admin/src/views/LqLaundrySupplier/form-dialog.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog :title="!dataForm.id ? '新增清洗商' : '编辑清洗商'" :close-on-click-modal="false" | |
| 3 | + :visible.sync="visible" class="NCC-dialog NCC-dialog_center" lock-scroll width="600px"> | |
| 4 | + <el-form ref="elForm" :model="dataForm" size="small" label-width="120px" label-position="right" :rules="rules"> | |
| 5 | + <el-row :gutter="15"> | |
| 6 | + <el-col :span="24"> | |
| 7 | + <el-form-item label="清洗商名称" prop="supplierName"> | |
| 8 | + <el-input v-model="dataForm.supplierName" placeholder="请输入清洗商名称" clearable | |
| 9 | + :style='{"width":"100%"}' /> | |
| 10 | + </el-form-item> | |
| 11 | + </el-col> | |
| 12 | + <el-col :span="24"> | |
| 13 | + <el-form-item label="产品类型" prop="productType"> | |
| 14 | + <el-select v-model="dataForm.productType" placeholder="请选择产品类型" clearable filterable | |
| 15 | + :style='{"width":"100%"}'> | |
| 16 | + <el-option v-for="item in productTypeOptions" :key="item.Value" :label="item.Name" :value="item.Name" /> | |
| 17 | + </el-select> | |
| 18 | + </el-form-item> | |
| 19 | + </el-col> | |
| 20 | + <el-col :span="24"> | |
| 21 | + <el-form-item label="清洗价格" prop="laundryPrice"> | |
| 22 | + <el-input-number v-model="dataForm.laundryPrice" placeholder="请输入清洗价格" :min="0" :precision="2" | |
| 23 | + :style='{"width":"100%"}' /> | |
| 24 | + </el-form-item> | |
| 25 | + </el-col> | |
| 26 | + <el-col :span="24"> | |
| 27 | + <el-form-item label="备注" prop="remark"> | |
| 28 | + <el-input v-model="dataForm.remark" type="textarea" :rows="4" placeholder="请输入备注信息" clearable | |
| 29 | + :style='{"width":"100%"}' /> | |
| 30 | + </el-form-item> | |
| 31 | + </el-col> | |
| 32 | + </el-row> | |
| 33 | + </el-form> | |
| 34 | + <span slot="footer" class="dialog-footer"> | |
| 35 | + <el-button @click="visible = false">取 消</el-button> | |
| 36 | + <el-button type="primary" @click="dataFormSubmit()" :loading="btnLoading">确 定</el-button> | |
| 37 | + </span> | |
| 38 | + </el-dialog> | |
| 39 | +</template> | |
| 40 | + | |
| 41 | +<script> | |
| 42 | +import request from '@/utils/request' | |
| 43 | + | |
| 44 | +export default { | |
| 45 | + data() { | |
| 46 | + return { | |
| 47 | + visible: false, | |
| 48 | + btnLoading: false, | |
| 49 | + productTypeOptions: [], | |
| 50 | + dataForm: { | |
| 51 | + id: undefined, | |
| 52 | + supplierName: '', | |
| 53 | + productType: '', | |
| 54 | + laundryPrice: 0, | |
| 55 | + remark: '' | |
| 56 | + }, | |
| 57 | + rules: { | |
| 58 | + supplierName: [ | |
| 59 | + { required: true, message: '请输入清洗商名称', trigger: 'blur' } | |
| 60 | + ], | |
| 61 | + productType: [ | |
| 62 | + { required: true, message: '请选择产品类型', trigger: 'change' } | |
| 63 | + ], | |
| 64 | + laundryPrice: [ | |
| 65 | + { required: true, message: '请输入清洗价格', trigger: 'blur' }, | |
| 66 | + { type: 'number', min: 0, message: '清洗价格不能小于0', trigger: 'blur' } | |
| 67 | + ] | |
| 68 | + } | |
| 69 | + } | |
| 70 | + }, | |
| 71 | + methods: { | |
| 72 | + init(id) { | |
| 73 | + this.visible = true | |
| 74 | + this.dataForm = { | |
| 75 | + id: undefined, | |
| 76 | + supplierName: '', | |
| 77 | + productType: '', | |
| 78 | + laundryPrice: 0, | |
| 79 | + remark: '' | |
| 80 | + } | |
| 81 | + this.loadProductTypeOptions() | |
| 82 | + if (id) { | |
| 83 | + // 编辑模式,获取详情 | |
| 84 | + this.getDetail(id) | |
| 85 | + } | |
| 86 | + }, | |
| 87 | + // 加载产品类型选项 | |
| 88 | + loadProductTypeOptions() { | |
| 89 | + request({ | |
| 90 | + url: '/api/Extend/LqStoreConsumableInventory/consumable-product-type', | |
| 91 | + method: 'GET' | |
| 92 | + }).then(res => { | |
| 93 | + if (res.code == 200 && res.data && Array.isArray(res.data)) { | |
| 94 | + this.productTypeOptions = res.data | |
| 95 | + } else { | |
| 96 | + this.productTypeOptions = [] | |
| 97 | + } | |
| 98 | + }).catch(() => { | |
| 99 | + this.productTypeOptions = [] | |
| 100 | + }) | |
| 101 | + }, | |
| 102 | + // 获取详情 | |
| 103 | + getDetail(id) { | |
| 104 | + request({ | |
| 105 | + url: `/api/Extend/LqLaundrySupplier/${id}`, | |
| 106 | + method: 'GET' | |
| 107 | + }).then(res => { | |
| 108 | + if (res.code == 200 && res.data) { | |
| 109 | + const data = res.data | |
| 110 | + this.dataForm = { | |
| 111 | + id: data.id, | |
| 112 | + supplierName: data.supplierName || '', | |
| 113 | + productType: data.productType || '', | |
| 114 | + laundryPrice: data.laundryPrice || 0, | |
| 115 | + remark: data.remark || '' | |
| 116 | + } | |
| 117 | + } | |
| 118 | + }).catch(() => { | |
| 119 | + this.$message({ | |
| 120 | + type: 'error', | |
| 121 | + message: '获取详情失败' | |
| 122 | + }) | |
| 123 | + }) | |
| 124 | + }, | |
| 125 | + // 提交表单 | |
| 126 | + dataFormSubmit() { | |
| 127 | + this.$refs.elForm.validate((valid) => { | |
| 128 | + if (!valid) { | |
| 129 | + return false | |
| 130 | + } | |
| 131 | + this.btnLoading = true | |
| 132 | + const url = this.dataForm.id ? '/api/Extend/LqLaundrySupplier/Update' : '/api/Extend/LqLaundrySupplier/Create' | |
| 133 | + const method = this.dataForm.id ? 'PUT' : 'POST' | |
| 134 | + const data = this.dataForm.id | |
| 135 | + ? { | |
| 136 | + id: this.dataForm.id, | |
| 137 | + supplierName: this.dataForm.supplierName, | |
| 138 | + productType: this.dataForm.productType, | |
| 139 | + laundryPrice: this.dataForm.laundryPrice, | |
| 140 | + remark: this.dataForm.remark | |
| 141 | + } | |
| 142 | + : { | |
| 143 | + supplierName: this.dataForm.supplierName, | |
| 144 | + productType: this.dataForm.productType, | |
| 145 | + laundryPrice: this.dataForm.laundryPrice, | |
| 146 | + remark: this.dataForm.remark | |
| 147 | + } | |
| 148 | + request({ | |
| 149 | + url: url, | |
| 150 | + method: method, | |
| 151 | + data: data | |
| 152 | + }).then(res => { | |
| 153 | + this.btnLoading = false | |
| 154 | + this.$message({ | |
| 155 | + type: 'success', | |
| 156 | + message: res.msg || (this.dataForm.id ? '编辑成功' : '创建成功'), | |
| 157 | + onClose: () => { | |
| 158 | + this.visible = false | |
| 159 | + this.$emit('refresh') | |
| 160 | + } | |
| 161 | + }) | |
| 162 | + }).catch(() => { | |
| 163 | + this.btnLoading = false | |
| 164 | + }) | |
| 165 | + }) | |
| 166 | + } | |
| 167 | + } | |
| 168 | +} | |
| 169 | +</script> | |
| 170 | + | |
| 171 | +<style lang="scss" scoped> | |
| 172 | +</style> | |
| 173 | + | ... | ... |
antis-ncc-admin/src/views/LqLaundrySupplier/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.supplierName" 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.productType" 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.isEffective" placeholder="是否有效" clearable> | |
| 20 | + <el-option label="有效" :value="1" /> | |
| 21 | + <el-option label="无效" :value="0" /> | |
| 22 | + </el-select> | |
| 23 | + </el-form-item> | |
| 24 | + </el-col> | |
| 25 | + <el-col :span="6"> | |
| 26 | + <el-form-item> | |
| 27 | + <el-button type="primary" icon="el-icon-search" @click="search()">查询</el-button> | |
| 28 | + <el-button icon="el-icon-refresh-right" @click="reset()">重置</el-button> | |
| 29 | + </el-form-item> | |
| 30 | + </el-col> | |
| 31 | + </el-form> | |
| 32 | + </el-row> | |
| 33 | + <!-- 列表 --> | |
| 34 | + <div class="NCC-common-layout-main NCC-flex-main"> | |
| 35 | + <div class="NCC-common-head"> | |
| 36 | + <div> | |
| 37 | + <el-button type="primary" icon="el-icon-plus" @click="add()">新增</el-button> | |
| 38 | + </div> | |
| 39 | + <div class="NCC-common-head-right"> | |
| 40 | + <el-tooltip effect="dark" content="刷新" placement="top"> | |
| 41 | + <el-link icon="icon-ym icon-ym-Refresh NCC-common-head-icon" :underline="false" | |
| 42 | + @click="initData()" /> | |
| 43 | + </el-tooltip> | |
| 44 | + <screenfull isContainer /> | |
| 45 | + </div> | |
| 46 | + </div> | |
| 47 | + <NCC-table v-loading="loading" :data="list" | |
| 48 | + :header-cell-style="{ background: '#f5f7fa', color: '#606266' }"> | |
| 49 | + <el-table-column label="清洗商名称" align="center"> | |
| 50 | + <template slot-scope="scope"> | |
| 51 | + <div class="supplier-info"> | |
| 52 | + <i class="el-icon-office-building supplier-icon"></i> | |
| 53 | + <span class="text-nowrap">{{ scope.row.supplierName || '无' }}</span> | |
| 54 | + </div> | |
| 55 | + </template> | |
| 56 | + </el-table-column> | |
| 57 | + <el-table-column label="产品类型" align="center"> | |
| 58 | + <template slot-scope="scope"> | |
| 59 | + <div class="product-type-info"> | |
| 60 | + <i class="el-icon-goods product-type-icon"></i> | |
| 61 | + <span class="text-nowrap">{{ scope.row.productType || '无' }}</span> | |
| 62 | + </div> | |
| 63 | + </template> | |
| 64 | + </el-table-column> | |
| 65 | + <el-table-column label="清洗价格" align="center"> | |
| 66 | + <template slot-scope="scope"> | |
| 67 | + <div class="price-info"> | |
| 68 | + <i class="el-icon-coin price-icon"></i> | |
| 69 | + <span class="text-nowrap">{{ scope.row.laundryPrice || 0 }}</span> | |
| 70 | + </div> | |
| 71 | + </template> | |
| 72 | + </el-table-column> | |
| 73 | + <el-table-column label="备注" align="center" min-width="150"> | |
| 74 | + <template slot-scope="scope"> | |
| 75 | + <div class="remark-info"> | |
| 76 | + <i class="el-icon-document remark-icon"></i> | |
| 77 | + <span class="text-nowrap">{{ scope.row.remark || '无' }}</span> | |
| 78 | + </div> | |
| 79 | + </template> | |
| 80 | + </el-table-column> | |
| 81 | + <el-table-column label="是否有效" align="center"> | |
| 82 | + <template slot-scope="scope"> | |
| 83 | + <el-tag :type="scope.row.isEffective === 1 ? 'success' : 'info'" size="small"> | |
| 84 | + {{ scope.row.isEffective === 1 ? '有效' : '无效' }} | |
| 85 | + </el-tag> | |
| 86 | + </template> | |
| 87 | + </el-table-column> | |
| 88 | + <el-table-column label="创建人" align="center"> | |
| 89 | + <template slot-scope="scope"> | |
| 90 | + <div class="user-info"> | |
| 91 | + <i class="el-icon-user user-icon"></i> | |
| 92 | + <span class="text-nowrap">{{ scope.row.createUserName || '无' }}</span> | |
| 93 | + </div> | |
| 94 | + </template> | |
| 95 | + </el-table-column> | |
| 96 | + <el-table-column label="创建时间" align="center" width="180" prop="createTime"> | |
| 97 | + <template slot-scope="scope"> | |
| 98 | + <div class="time-info"> | |
| 99 | + <i class="el-icon-time time-icon"></i> | |
| 100 | + <span class="text-nowrap">{{ formatDateTime(scope.row.createTime) || '无' }}</span> | |
| 101 | + </div> | |
| 102 | + </template> | |
| 103 | + </el-table-column> | |
| 104 | + <el-table-column label="更新人" align="center"> | |
| 105 | + <template slot-scope="scope"> | |
| 106 | + <div class="user-info"> | |
| 107 | + <i class="el-icon-user user-icon"></i> | |
| 108 | + <span class="text-nowrap">{{ scope.row.updateUserName || '无' }}</span> | |
| 109 | + </div> | |
| 110 | + </template> | |
| 111 | + </el-table-column> | |
| 112 | + <el-table-column label="更新时间" align="center" width="180" prop="updateTime"> | |
| 113 | + <template slot-scope="scope"> | |
| 114 | + <div class="time-info"> | |
| 115 | + <i class="el-icon-time time-icon"></i> | |
| 116 | + <span class="text-nowrap">{{ formatDateTime(scope.row.updateTime) || '无' }}</span> | |
| 117 | + </div> | |
| 118 | + </template> | |
| 119 | + </el-table-column> | |
| 120 | + <el-table-column label="操作" width="180" align="left" fixed="right"> | |
| 121 | + <template slot-scope="scope"> | |
| 122 | + <div class="action-buttons"> | |
| 123 | + <el-button type="text" icon="el-icon-edit" @click="edit(scope.row)" class="edit-btn"> | |
| 124 | + 编辑 | |
| 125 | + </el-button> | |
| 126 | + <el-button type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" | |
| 127 | + class="delete-btn"> | |
| 128 | + 删除 | |
| 129 | + </el-button> | |
| 130 | + </div> | |
| 131 | + </template> | |
| 132 | + </el-table-column> | |
| 133 | + </NCC-table> | |
| 134 | + <pagination :total="total" :page.sync="query.currentPage" | |
| 135 | + :limit.sync="query.pageSize" @pagination="initData" /> | |
| 136 | + </div> | |
| 137 | + </div> | |
| 138 | + <!-- 表单弹窗 --> | |
| 139 | + <FormDialog v-if="formVisible" ref="FormDialog" @refresh="refresh" /> | |
| 140 | + </div> | |
| 141 | +</template> | |
| 142 | + | |
| 143 | +<script> | |
| 144 | +import request from '@/utils/request' | |
| 145 | +import FormDialog from './form-dialog.vue' | |
| 146 | + | |
| 147 | +export default { | |
| 148 | + components: { FormDialog }, | |
| 149 | + data() { | |
| 150 | + return { | |
| 151 | + list: [], | |
| 152 | + loading: false, | |
| 153 | + total: 0, | |
| 154 | + query: { | |
| 155 | + currentPage: 1, | |
| 156 | + pageSize: 20, | |
| 157 | + supplierName: undefined, | |
| 158 | + productType: undefined, | |
| 159 | + isEffective: undefined | |
| 160 | + }, | |
| 161 | + formVisible: false | |
| 162 | + } | |
| 163 | + }, | |
| 164 | + created() { | |
| 165 | + this.initData() | |
| 166 | + }, | |
| 167 | + methods: { | |
| 168 | + // 初始化数据 | |
| 169 | + initData() { | |
| 170 | + this.loading = true | |
| 171 | + let query = { ...this.query } | |
| 172 | + // 移除空值 | |
| 173 | + Object.keys(query).forEach(key => { | |
| 174 | + if (query[key] === undefined || query[key] === null || query[key] === '') { | |
| 175 | + delete query[key] | |
| 176 | + } | |
| 177 | + }) | |
| 178 | + request({ | |
| 179 | + url: '/api/Extend/LqLaundrySupplier/GetList', | |
| 180 | + method: 'GET', | |
| 181 | + data: query | |
| 182 | + }).then(res => { | |
| 183 | + if (res.code == 200 && res.data) { | |
| 184 | + this.list = res.data.list || [] | |
| 185 | + this.total = res.data.pagination ? res.data.pagination.total : 0 | |
| 186 | + } else { | |
| 187 | + this.list = [] | |
| 188 | + this.total = 0 | |
| 189 | + } | |
| 190 | + this.loading = false | |
| 191 | + }).catch(() => { | |
| 192 | + this.loading = false | |
| 193 | + this.list = [] | |
| 194 | + this.total = 0 | |
| 195 | + }) | |
| 196 | + }, | |
| 197 | + // 查询 | |
| 198 | + search() { | |
| 199 | + this.query.currentPage = 1 | |
| 200 | + this.initData() | |
| 201 | + }, | |
| 202 | + // 重置 | |
| 203 | + reset() { | |
| 204 | + this.query = { | |
| 205 | + currentPage: 1, | |
| 206 | + pageSize: 20, | |
| 207 | + supplierName: undefined, | |
| 208 | + productType: undefined, | |
| 209 | + isEffective: undefined | |
| 210 | + } | |
| 211 | + this.initData() | |
| 212 | + }, | |
| 213 | + // 新增 | |
| 214 | + add() { | |
| 215 | + this.formVisible = true | |
| 216 | + this.$nextTick(() => { | |
| 217 | + this.$refs.FormDialog.init() | |
| 218 | + }) | |
| 219 | + }, | |
| 220 | + // 编辑 | |
| 221 | + edit(row) { | |
| 222 | + this.formVisible = true | |
| 223 | + this.$nextTick(() => { | |
| 224 | + this.$refs.FormDialog.init(row.id) | |
| 225 | + }) | |
| 226 | + }, | |
| 227 | + // 删除 | |
| 228 | + handleDelete(row) { | |
| 229 | + this.$confirm(`确定要删除清洗商【${row.supplierName}】的${row.productType}记录吗?`, '提示', { | |
| 230 | + type: 'warning' | |
| 231 | + }).then(() => { | |
| 232 | + request({ | |
| 233 | + url: `/api/Extend/LqLaundrySupplier/${row.id}`, | |
| 234 | + method: 'DELETE' | |
| 235 | + }).then(res => { | |
| 236 | + this.$message({ | |
| 237 | + type: 'success', | |
| 238 | + message: res.msg || '删除成功', | |
| 239 | + onClose: () => { | |
| 240 | + this.initData() | |
| 241 | + } | |
| 242 | + }) | |
| 243 | + }) | |
| 244 | + }).catch(() => { }) | |
| 245 | + }, | |
| 246 | + // 刷新 | |
| 247 | + refresh() { | |
| 248 | + this.formVisible = false | |
| 249 | + this.initData() | |
| 250 | + }, | |
| 251 | + // 格式化日期时间 | |
| 252 | + formatDateTime(dateTime) { | |
| 253 | + if (!dateTime) return '' | |
| 254 | + const date = new Date(dateTime) | |
| 255 | + const year = date.getFullYear() | |
| 256 | + const month = String(date.getMonth() + 1).padStart(2, '0') | |
| 257 | + const day = String(date.getDate()).padStart(2, '0') | |
| 258 | + const hours = String(date.getHours()).padStart(2, '0') | |
| 259 | + const minutes = String(date.getMinutes()).padStart(2, '0') | |
| 260 | + const seconds = String(date.getSeconds()).padStart(2, '0') | |
| 261 | + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}` | |
| 262 | + } | |
| 263 | + } | |
| 264 | +} | |
| 265 | +</script> | |
| 266 | + | |
| 267 | +<style lang="scss" scoped> | |
| 268 | +// 通用文本不换行样式 | |
| 269 | +.text-nowrap { | |
| 270 | + white-space: nowrap; | |
| 271 | + overflow: hidden; | |
| 272 | + text-overflow: ellipsis; | |
| 273 | + max-width: 100%; | |
| 274 | +} | |
| 275 | + | |
| 276 | +// 信息显示样式 | |
| 277 | +.supplier-info, | |
| 278 | +.product-type-info, | |
| 279 | +.price-info, | |
| 280 | +.remark-info, | |
| 281 | +.user-info, | |
| 282 | +.time-info { | |
| 283 | + display: flex; | |
| 284 | + align-items: center; | |
| 285 | + justify-content: center; | |
| 286 | + gap: 6px; | |
| 287 | +} | |
| 288 | + | |
| 289 | +.supplier-icon { | |
| 290 | + color: #409EFF; | |
| 291 | + font-size: 16px; | |
| 292 | +} | |
| 293 | + | |
| 294 | +.product-type-icon { | |
| 295 | + color: #67C23A; | |
| 296 | + font-size: 16px; | |
| 297 | +} | |
| 298 | + | |
| 299 | +.price-icon { | |
| 300 | + color: #E6A23C; | |
| 301 | + font-size: 16px; | |
| 302 | +} | |
| 303 | + | |
| 304 | +.remark-icon { | |
| 305 | + color: #909399; | |
| 306 | + font-size: 16px; | |
| 307 | +} | |
| 308 | + | |
| 309 | +.user-icon { | |
| 310 | + color: #909399; | |
| 311 | + font-size: 16px; | |
| 312 | +} | |
| 313 | + | |
| 314 | +.time-icon { | |
| 315 | + color: #909399; | |
| 316 | + font-size: 16px; | |
| 317 | +} | |
| 318 | + | |
| 319 | +// 操作按钮样式 | |
| 320 | +.action-buttons { | |
| 321 | + display: flex; | |
| 322 | + align-items: center; | |
| 323 | + gap: 8px; | |
| 324 | +} | |
| 325 | + | |
| 326 | +.edit-btn { | |
| 327 | + color: #409EFF; | |
| 328 | + | |
| 329 | + &:hover { | |
| 330 | + color: #66b1ff; | |
| 331 | + } | |
| 332 | +} | |
| 333 | + | |
| 334 | +.delete-btn { | |
| 335 | + color: #F56C6C !important; | |
| 336 | + | |
| 337 | + &:hover { | |
| 338 | + color: #f56c6c !important; | |
| 339 | + opacity: 0.8; | |
| 340 | + } | |
| 341 | +} | |
| 342 | + | |
| 343 | +// 表格行悬停效果 | |
| 344 | +::v-deep .el-table__row:hover { | |
| 345 | + background-color: #f5f7fa; | |
| 346 | +} | |
| 347 | + | |
| 348 | +// 表格头部样式 | |
| 349 | +::v-deep .el-table__header-wrapper { | |
| 350 | + .el-table__header { | |
| 351 | + th { | |
| 352 | + background-color: #f5f7fa; | |
| 353 | + color: #606266; | |
| 354 | + font-weight: 600; | |
| 355 | + } | |
| 356 | + } | |
| 357 | +} | |
| 358 | +</style> | |
| 359 | + | ... | ... |
antis-ncc-admin/src/views/LqStoreConsumableInventory/form-dialog.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog :title="!dataForm.id ? '新增门店消耗品库存' : '编辑门店消耗品库存'" :close-on-click-modal="false" | |
| 3 | + :visible.sync="visible" class="NCC-dialog NCC-dialog_center" lock-scroll width="600px"> | |
| 4 | + <el-form ref="elForm" :model="dataForm" size="small" label-width="120px" label-position="right" :rules="rules"> | |
| 5 | + <el-row :gutter="15"> | |
| 6 | + <el-col :span="24"> | |
| 7 | + <el-form-item label="门店" prop="storeId"> | |
| 8 | + <el-select v-model="dataForm.storeId" placeholder="请选择门店" clearable filterable | |
| 9 | + :style='{"width":"100%"}' :disabled="!!dataForm.id"> | |
| 10 | + <el-option v-for="store in storeOptions" :key="store.id" :label="store.dm" :value="store.id" /> | |
| 11 | + </el-select> | |
| 12 | + </el-form-item> | |
| 13 | + </el-col> | |
| 14 | + <el-col :span="24"> | |
| 15 | + <el-form-item label="产品类型" prop="productType"> | |
| 16 | + <el-select v-model="dataForm.productType" placeholder="请选择产品类型" clearable filterable | |
| 17 | + :style='{"width":"100%"}'> | |
| 18 | + <el-option v-for="item in productTypeOptions" :key="item.Value" :label="item.Name" :value="item.Name" /> | |
| 19 | + </el-select> | |
| 20 | + </el-form-item> | |
| 21 | + </el-col> | |
| 22 | + <el-col :span="24"> | |
| 23 | + <el-form-item label="库存数量" prop="quantity"> | |
| 24 | + <el-input-number v-model="dataForm.quantity" placeholder="请输入库存数量" :min="0" :precision="0" | |
| 25 | + :style='{"width":"100%"}' /> | |
| 26 | + </el-form-item> | |
| 27 | + </el-col> | |
| 28 | + </el-row> | |
| 29 | + </el-form> | |
| 30 | + <span slot="footer" class="dialog-footer"> | |
| 31 | + <el-button @click="visible = false">取 消</el-button> | |
| 32 | + <el-button type="primary" @click="dataFormSubmit()" :loading="btnLoading">确 定</el-button> | |
| 33 | + </span> | |
| 34 | + </el-dialog> | |
| 35 | +</template> | |
| 36 | + | |
| 37 | +<script> | |
| 38 | +import request from '@/utils/request' | |
| 39 | + | |
| 40 | +export default { | |
| 41 | + data() { | |
| 42 | + return { | |
| 43 | + visible: false, | |
| 44 | + btnLoading: false, | |
| 45 | + storeOptions: [], | |
| 46 | + productTypeOptions: [], | |
| 47 | + dataForm: { | |
| 48 | + id: undefined, | |
| 49 | + storeId: '', | |
| 50 | + productType: '', | |
| 51 | + quantity: 0 | |
| 52 | + }, | |
| 53 | + rules: { | |
| 54 | + storeId: [ | |
| 55 | + { required: true, message: '请选择门店', trigger: 'change' } | |
| 56 | + ], | |
| 57 | + productType: [ | |
| 58 | + { required: true, message: '请选择产品类型', trigger: 'change' } | |
| 59 | + ], | |
| 60 | + quantity: [ | |
| 61 | + { required: true, message: '请输入库存数量', trigger: 'blur' }, | |
| 62 | + { type: 'number', min: 0, message: '库存数量不能小于0', trigger: 'blur' } | |
| 63 | + ] | |
| 64 | + } | |
| 65 | + } | |
| 66 | + }, | |
| 67 | + methods: { | |
| 68 | + init(id) { | |
| 69 | + this.visible = true | |
| 70 | + this.dataForm = { | |
| 71 | + id: undefined, | |
| 72 | + storeId: '', | |
| 73 | + productType: '', | |
| 74 | + quantity: 0 | |
| 75 | + } | |
| 76 | + this.loadStoreOptions() | |
| 77 | + this.loadProductTypeOptions() | |
| 78 | + if (id) { | |
| 79 | + // 编辑模式,获取详情 | |
| 80 | + this.getDetail(id) | |
| 81 | + } | |
| 82 | + }, | |
| 83 | + // 加载门店选项 | |
| 84 | + loadStoreOptions() { | |
| 85 | + request({ | |
| 86 | + url: '/api/Extend/LqMdxx', | |
| 87 | + method: 'GET', | |
| 88 | + data: { | |
| 89 | + currentPage: 1, | |
| 90 | + pageSize: 1000 | |
| 91 | + } | |
| 92 | + }).then(res => { | |
| 93 | + if (res.code == 200 && res.data && res.data.list) { | |
| 94 | + this.storeOptions = res.data.list | |
| 95 | + } else { | |
| 96 | + this.storeOptions = [] | |
| 97 | + } | |
| 98 | + }).catch(() => { | |
| 99 | + this.storeOptions = [] | |
| 100 | + }) | |
| 101 | + }, | |
| 102 | + // 加载产品类型选项 | |
| 103 | + loadProductTypeOptions() { | |
| 104 | + request({ | |
| 105 | + url: '/api/Extend/LqStoreConsumableInventory/consumable-product-type', | |
| 106 | + method: 'GET' | |
| 107 | + }).then(res => { | |
| 108 | + if (res.code == 200 && res.data && Array.isArray(res.data)) { | |
| 109 | + this.productTypeOptions = res.data | |
| 110 | + } else { | |
| 111 | + this.productTypeOptions = [] | |
| 112 | + } | |
| 113 | + }).catch(() => { | |
| 114 | + this.productTypeOptions = [] | |
| 115 | + }) | |
| 116 | + }, | |
| 117 | + // 获取详情 | |
| 118 | + getDetail(id) { | |
| 119 | + request({ | |
| 120 | + url: `/api/Extend/LqStoreConsumableInventory/${id}`, | |
| 121 | + method: 'GET' | |
| 122 | + }).then(res => { | |
| 123 | + if (res.code == 200 && res.data) { | |
| 124 | + const data = res.data | |
| 125 | + this.dataForm = { | |
| 126 | + id: data.id, | |
| 127 | + storeId: data.storeId || '', | |
| 128 | + productType: data.productType || '', | |
| 129 | + quantity: data.quantity || 0 | |
| 130 | + } | |
| 131 | + } | |
| 132 | + }).catch(() => { | |
| 133 | + this.$message({ | |
| 134 | + type: 'error', | |
| 135 | + message: '获取详情失败' | |
| 136 | + }) | |
| 137 | + }) | |
| 138 | + }, | |
| 139 | + // 提交表单 | |
| 140 | + dataFormSubmit() { | |
| 141 | + this.$refs.elForm.validate((valid) => { | |
| 142 | + if (!valid) { | |
| 143 | + return false | |
| 144 | + } | |
| 145 | + this.btnLoading = true | |
| 146 | + const url = this.dataForm.id ? '/api/Extend/LqStoreConsumableInventory/Update' : '/api/Extend/LqStoreConsumableInventory/Create' | |
| 147 | + const method = this.dataForm.id ? 'PUT' : 'POST' | |
| 148 | + const data = this.dataForm.id | |
| 149 | + ? { | |
| 150 | + id: this.dataForm.id, | |
| 151 | + storeId: this.dataForm.storeId, | |
| 152 | + productType: this.dataForm.productType, | |
| 153 | + quantity: this.dataForm.quantity | |
| 154 | + } | |
| 155 | + : { | |
| 156 | + storeId: this.dataForm.storeId, | |
| 157 | + productType: this.dataForm.productType, | |
| 158 | + quantity: this.dataForm.quantity | |
| 159 | + } | |
| 160 | + request({ | |
| 161 | + url: url, | |
| 162 | + method: method, | |
| 163 | + data: data | |
| 164 | + }).then(res => { | |
| 165 | + this.btnLoading = false | |
| 166 | + this.$message({ | |
| 167 | + type: 'success', | |
| 168 | + message: res.msg || (this.dataForm.id ? '编辑成功' : '创建成功'), | |
| 169 | + onClose: () => { | |
| 170 | + this.visible = false | |
| 171 | + this.$emit('refresh') | |
| 172 | + } | |
| 173 | + }) | |
| 174 | + }).catch(() => { | |
| 175 | + this.btnLoading = false | |
| 176 | + }) | |
| 177 | + }) | |
| 178 | + } | |
| 179 | + } | |
| 180 | +} | |
| 181 | +</script> | |
| 182 | + | |
| 183 | +<style lang="scss" scoped> | |
| 184 | +</style> | |
| 185 | + | ... | ... |
antis-ncc-admin/src/views/LqStoreConsumableInventory/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-select v-model="query.storeId" placeholder="请选择门店" clearable filterable> | |
| 10 | + <el-option v-for="store in storeOptions" :key="store.id" :label="store.dm" :value="store.id" /> | |
| 11 | + </el-select> | |
| 12 | + </el-form-item> | |
| 13 | + </el-col> | |
| 14 | + <el-col :span="6"> | |
| 15 | + <el-form-item label="产品类型"> | |
| 16 | + <el-input v-model="query.productType" placeholder="产品类型" clearable /> | |
| 17 | + </el-form-item> | |
| 18 | + </el-col> | |
| 19 | + <el-col :span="6"> | |
| 20 | + <el-form-item label="是否有效"> | |
| 21 | + <el-select v-model="query.isEffective" placeholder="是否有效" clearable> | |
| 22 | + <el-option label="有效" :value="1" /> | |
| 23 | + <el-option label="无效" :value="0" /> | |
| 24 | + </el-select> | |
| 25 | + </el-form-item> | |
| 26 | + </el-col> | |
| 27 | + <el-col :span="6"> | |
| 28 | + <el-form-item> | |
| 29 | + <el-button type="primary" icon="el-icon-search" @click="search()">查询</el-button> | |
| 30 | + <el-button icon="el-icon-refresh-right" @click="reset()">重置</el-button> | |
| 31 | + </el-form-item> | |
| 32 | + </el-col> | |
| 33 | + </el-form> | |
| 34 | + </el-row> | |
| 35 | + <!-- 列表 --> | |
| 36 | + <div class="NCC-common-layout-main NCC-flex-main"> | |
| 37 | + <div class="NCC-common-head"> | |
| 38 | + <div> | |
| 39 | + <el-button type="primary" icon="el-icon-plus" @click="add()">新增</el-button> | |
| 40 | + </div> | |
| 41 | + <div class="NCC-common-head-right"> | |
| 42 | + <el-tooltip effect="dark" content="刷新" placement="top"> | |
| 43 | + <el-link icon="icon-ym icon-ym-Refresh NCC-common-head-icon" :underline="false" | |
| 44 | + @click="initData()" /> | |
| 45 | + </el-tooltip> | |
| 46 | + <screenfull isContainer /> | |
| 47 | + </div> | |
| 48 | + </div> | |
| 49 | + <NCC-table v-loading="loading" :data="list" | |
| 50 | + :header-cell-style="{ background: '#f5f7fa', color: '#606266' }"> | |
| 51 | + <el-table-column label="门店名称" align="center" > | |
| 52 | + <template slot-scope="scope"> | |
| 53 | + <div class="store-info"> | |
| 54 | + <i class="el-icon-office-building store-icon"></i> | |
| 55 | + <span class="text-nowrap">{{ scope.row.storeName || '无' }}</span> | |
| 56 | + </div> | |
| 57 | + </template> | |
| 58 | + </el-table-column> | |
| 59 | + <el-table-column label="产品类型" align="center" > | |
| 60 | + <template slot-scope="scope"> | |
| 61 | + <div class="product-type-info"> | |
| 62 | + <i class="el-icon-goods product-type-icon"></i> | |
| 63 | + <span class="text-nowrap">{{ scope.row.productType || '无' }}</span> | |
| 64 | + </div> | |
| 65 | + </template> | |
| 66 | + </el-table-column> | |
| 67 | + <el-table-column label="库存数量" align="center" > | |
| 68 | + <template slot-scope="scope"> | |
| 69 | + <div class="quantity-info"> | |
| 70 | + <i class="el-icon-box quantity-icon"></i> | |
| 71 | + <span class="text-nowrap">{{ scope.row.quantity || 0 }}</span> | |
| 72 | + </div> | |
| 73 | + </template> | |
| 74 | + </el-table-column> | |
| 75 | + <el-table-column label="是否有效" align="center" > | |
| 76 | + <template slot-scope="scope"> | |
| 77 | + <el-tag :type="scope.row.isEffective === 1 ? 'success' : 'info'" size="small"> | |
| 78 | + {{ scope.row.isEffective === 1 ? '有效' : '无效' }} | |
| 79 | + </el-tag> | |
| 80 | + </template> | |
| 81 | + </el-table-column> | |
| 82 | + <el-table-column label="创建人" align="center" > | |
| 83 | + <template slot-scope="scope"> | |
| 84 | + <div class="user-info"> | |
| 85 | + <i class="el-icon-user user-icon"></i> | |
| 86 | + <span class="text-nowrap">{{ scope.row.createUserName || '无' }}</span> | |
| 87 | + </div> | |
| 88 | + </template> | |
| 89 | + </el-table-column> | |
| 90 | + <el-table-column label="创建时间" align="center" width="180" prop="createTime"> | |
| 91 | + <template slot-scope="scope"> | |
| 92 | + <div class="time-info"> | |
| 93 | + <i class="el-icon-time time-icon"></i> | |
| 94 | + <span class="text-nowrap">{{ formatDateTime(scope.row.createTime) || '无' }}</span> | |
| 95 | + </div> | |
| 96 | + </template> | |
| 97 | + </el-table-column> | |
| 98 | + <el-table-column label="更新人" align="center" > | |
| 99 | + <template slot-scope="scope"> | |
| 100 | + <div class="user-info"> | |
| 101 | + <i class="el-icon-user user-icon"></i> | |
| 102 | + <span class="text-nowrap">{{ scope.row.updateUserName || '无' }}</span> | |
| 103 | + </div> | |
| 104 | + </template> | |
| 105 | + </el-table-column> | |
| 106 | + <el-table-column label="更新时间" align="center" width="180" prop="updateTime"> | |
| 107 | + <template slot-scope="scope"> | |
| 108 | + <div class="time-info"> | |
| 109 | + <i class="el-icon-time time-icon"></i> | |
| 110 | + <span class="text-nowrap">{{ formatDateTime(scope.row.updateTime) || '无' }}</span> | |
| 111 | + </div> | |
| 112 | + </template> | |
| 113 | + </el-table-column> | |
| 114 | + <el-table-column label="操作" width="180" align="left" fixed="right"> | |
| 115 | + <template slot-scope="scope"> | |
| 116 | + <div class="action-buttons"> | |
| 117 | + <el-button type="text" icon="el-icon-edit" @click="edit(scope.row)" class="edit-btn"> | |
| 118 | + 编辑 | |
| 119 | + </el-button> | |
| 120 | + <el-button type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" | |
| 121 | + class="delete-btn"> | |
| 122 | + 删除 | |
| 123 | + </el-button> | |
| 124 | + </div> | |
| 125 | + </template> | |
| 126 | + </el-table-column> | |
| 127 | + </NCC-table> | |
| 128 | + <pagination :total="total" :page.sync="query.currentPage" | |
| 129 | + :limit.sync="query.pageSize" @pagination="initData" /> | |
| 130 | + </div> | |
| 131 | + </div> | |
| 132 | + <!-- 表单弹窗 --> | |
| 133 | + <FormDialog v-if="formVisible" ref="FormDialog" @refresh="refresh" /> | |
| 134 | + </div> | |
| 135 | +</template> | |
| 136 | + | |
| 137 | +<script> | |
| 138 | +import request from '@/utils/request' | |
| 139 | +import FormDialog from './form-dialog.vue' | |
| 140 | + | |
| 141 | +export default { | |
| 142 | + components: { FormDialog }, | |
| 143 | + data() { | |
| 144 | + return { | |
| 145 | + list: [], | |
| 146 | + loading: false, | |
| 147 | + total: 0, | |
| 148 | + storeOptions: [], | |
| 149 | + query: { | |
| 150 | + currentPage: 1, | |
| 151 | + pageSize: 20, | |
| 152 | + storeId: undefined, | |
| 153 | + productType: undefined, | |
| 154 | + isEffective: undefined | |
| 155 | + }, | |
| 156 | + formVisible: false | |
| 157 | + } | |
| 158 | + }, | |
| 159 | + created() { | |
| 160 | + this.initData() | |
| 161 | + this.loadStoreOptions() | |
| 162 | + }, | |
| 163 | + methods: { | |
| 164 | + // 初始化数据 | |
| 165 | + initData() { | |
| 166 | + this.loading = true | |
| 167 | + let query = { ...this.query } | |
| 168 | + // 移除空值 | |
| 169 | + Object.keys(query).forEach(key => { | |
| 170 | + if (query[key] === undefined || query[key] === null || query[key] === '') { | |
| 171 | + delete query[key] | |
| 172 | + } | |
| 173 | + }) | |
| 174 | + request({ | |
| 175 | + url: '/api/Extend/LqStoreConsumableInventory/GetList', | |
| 176 | + method: 'GET', | |
| 177 | + data: query | |
| 178 | + }).then(res => { | |
| 179 | + if (res.code == 200 && res.data) { | |
| 180 | + this.list = res.data.list || [] | |
| 181 | + this.total = res.data.pagination ? res.data.pagination.total : 0 | |
| 182 | + } else { | |
| 183 | + this.list = [] | |
| 184 | + this.total = 0 | |
| 185 | + } | |
| 186 | + this.loading = false | |
| 187 | + }).catch(() => { | |
| 188 | + this.loading = false | |
| 189 | + this.list = [] | |
| 190 | + this.total = 0 | |
| 191 | + }) | |
| 192 | + }, | |
| 193 | + // 加载门店选项 | |
| 194 | + loadStoreOptions() { | |
| 195 | + request({ | |
| 196 | + url: '/api/Extend/LqMdxx', | |
| 197 | + method: 'GET', | |
| 198 | + data: { | |
| 199 | + currentPage: 1, | |
| 200 | + pageSize: 1000 | |
| 201 | + } | |
| 202 | + }).then(res => { | |
| 203 | + if (res.code == 200 && res.data && res.data.list) { | |
| 204 | + this.storeOptions = res.data.list | |
| 205 | + } else { | |
| 206 | + this.storeOptions = [] | |
| 207 | + } | |
| 208 | + }).catch(() => { | |
| 209 | + this.storeOptions = [] | |
| 210 | + }) | |
| 211 | + }, | |
| 212 | + // 查询 | |
| 213 | + search() { | |
| 214 | + this.query.currentPage = 1 | |
| 215 | + this.initData() | |
| 216 | + }, | |
| 217 | + // 重置 | |
| 218 | + reset() { | |
| 219 | + this.query = { | |
| 220 | + currentPage: 1, | |
| 221 | + pageSize: 20, | |
| 222 | + storeId: undefined, | |
| 223 | + productType: undefined, | |
| 224 | + isEffective: undefined | |
| 225 | + } | |
| 226 | + this.initData() | |
| 227 | + }, | |
| 228 | + // 新增 | |
| 229 | + add() { | |
| 230 | + this.formVisible = true | |
| 231 | + this.$nextTick(() => { | |
| 232 | + this.$refs.FormDialog.init() | |
| 233 | + }) | |
| 234 | + }, | |
| 235 | + // 编辑 | |
| 236 | + edit(row) { | |
| 237 | + this.formVisible = true | |
| 238 | + this.$nextTick(() => { | |
| 239 | + this.$refs.FormDialog.init(row.id) | |
| 240 | + }) | |
| 241 | + }, | |
| 242 | + // 删除 | |
| 243 | + handleDelete(row) { | |
| 244 | + this.$confirm(`确定要删除该库存记录吗?`, '提示', { | |
| 245 | + type: 'warning' | |
| 246 | + }).then(() => { | |
| 247 | + request({ | |
| 248 | + url: `/api/Extend/LqStoreConsumableInventory/${row.id}`, | |
| 249 | + method: 'DELETE' | |
| 250 | + }).then(res => { | |
| 251 | + this.$message({ | |
| 252 | + type: 'success', | |
| 253 | + message: res.msg || '删除成功', | |
| 254 | + onClose: () => { | |
| 255 | + this.initData() | |
| 256 | + } | |
| 257 | + }) | |
| 258 | + }) | |
| 259 | + }).catch(() => { }) | |
| 260 | + }, | |
| 261 | + // 刷新 | |
| 262 | + refresh() { | |
| 263 | + this.formVisible = false | |
| 264 | + this.initData() | |
| 265 | + }, | |
| 266 | + // 格式化日期时间 | |
| 267 | + formatDateTime(dateTime) { | |
| 268 | + if (!dateTime) return '' | |
| 269 | + const date = new Date(dateTime) | |
| 270 | + const year = date.getFullYear() | |
| 271 | + const month = String(date.getMonth() + 1).padStart(2, '0') | |
| 272 | + const day = String(date.getDate()).padStart(2, '0') | |
| 273 | + const hours = String(date.getHours()).padStart(2, '0') | |
| 274 | + const minutes = String(date.getMinutes()).padStart(2, '0') | |
| 275 | + const seconds = String(date.getSeconds()).padStart(2, '0') | |
| 276 | + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}` | |
| 277 | + } | |
| 278 | + } | |
| 279 | +} | |
| 280 | +</script> | |
| 281 | + | |
| 282 | +<style lang="scss" scoped> | |
| 283 | +// 通用文本不换行样式 | |
| 284 | +.text-nowrap { | |
| 285 | + white-space: nowrap; | |
| 286 | + overflow: hidden; | |
| 287 | + text-overflow: ellipsis; | |
| 288 | + max-width: 100%; | |
| 289 | +} | |
| 290 | + | |
| 291 | +// 信息显示样式 | |
| 292 | +.store-info, | |
| 293 | +.product-type-info, | |
| 294 | +.quantity-info, | |
| 295 | +.user-info, | |
| 296 | +.time-info { | |
| 297 | + display: flex; | |
| 298 | + align-items: center; | |
| 299 | + justify-content: center; | |
| 300 | + gap: 6px; | |
| 301 | +} | |
| 302 | + | |
| 303 | +.store-icon { | |
| 304 | + color: #409EFF; | |
| 305 | + font-size: 16px; | |
| 306 | +} | |
| 307 | + | |
| 308 | +.product-type-icon { | |
| 309 | + color: #67C23A; | |
| 310 | + font-size: 16px; | |
| 311 | +} | |
| 312 | + | |
| 313 | +.quantity-icon { | |
| 314 | + color: #E6A23C; | |
| 315 | + font-size: 16px; | |
| 316 | +} | |
| 317 | + | |
| 318 | +.user-icon { | |
| 319 | + color: #909399; | |
| 320 | + font-size: 16px; | |
| 321 | +} | |
| 322 | + | |
| 323 | +.time-icon { | |
| 324 | + color: #909399; | |
| 325 | + font-size: 16px; | |
| 326 | +} | |
| 327 | + | |
| 328 | +// 操作按钮样式 | |
| 329 | +.action-buttons { | |
| 330 | + display: flex; | |
| 331 | + align-items: center; | |
| 332 | + gap: 8px; | |
| 333 | +} | |
| 334 | + | |
| 335 | +.edit-btn { | |
| 336 | + color: #409EFF; | |
| 337 | + | |
| 338 | + &:hover { | |
| 339 | + color: #66b1ff; | |
| 340 | + } | |
| 341 | +} | |
| 342 | + | |
| 343 | +.delete-btn { | |
| 344 | + color: #F56C6C !important; | |
| 345 | + | |
| 346 | + &:hover { | |
| 347 | + color: #f56c6c !important; | |
| 348 | + opacity: 0.8; | |
| 349 | + } | |
| 350 | +} | |
| 351 | + | |
| 352 | +// 表格行悬停效果 | |
| 353 | +::v-deep .el-table__row:hover { | |
| 354 | + background-color: #f5f7fa; | |
| 355 | +} | |
| 356 | + | |
| 357 | +// 表格头部样式 | |
| 358 | +::v-deep .el-table__header-wrapper { | |
| 359 | + .el-table__header { | |
| 360 | + th { | |
| 361 | + background-color: #f5f7fa; | |
| 362 | + color: #606266; | |
| 363 | + font-weight: 600; | |
| 364 | + } | |
| 365 | + } | |
| 366 | +} | |
| 367 | +</style> | |
| 368 | + | ... | ... |
antis-ncc-admin/src/views/lqInventory/AddUsageRecordForm.vue renamed to antis-ncc-admin/src/views/lqInventory copy/AddUsageRecordForm.vue
antis-ncc-admin/src/views/lqInventory/InventoryForm.vue renamed to antis-ncc-admin/src/views/lqInventory copy/InventoryForm.vue
antis-ncc-admin/src/views/lqInventory/InventoryInfoDialog.vue renamed to antis-ncc-admin/src/views/lqInventory copy/InventoryInfoDialog.vue
antis-ncc-admin/src/views/lqInventory/UsageRecordDialog.vue renamed to antis-ncc-admin/src/views/lqInventory copy/UsageRecordDialog.vue
antis-ncc-admin/src/views/lqInventory copy/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" /> | |
| 60 | + <el-table-column prop="price" label="单价" align="left"> | |
| 61 | + <template slot-scope="scope"> | |
| 62 | + {{ formatMoney(scope.row.price) }} | |
| 63 | + </template> | |
| 64 | + </el-table-column> | |
| 65 | + <el-table-column prop="quantity" label="库存数量" align="left" > | |
| 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="left" /> | |
| 71 | + <el-table-column prop="totalValue" label="总价值" align="left"> | |
| 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" /> --> | |
| 77 | + <el-table-column prop="createTime" label="创建时间" align="left" :formatter="ncc.tableDateFormat"> | |
| 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 | +import request from '@/utils/request' | |
| 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 | + request({ | |
| 151 | + url: `/api/permission/Organize/96240625-934F-490B-8AA6-0BC775B18468/Department`, | |
| 152 | + method: 'GET', | |
| 153 | + }).then((res) => { | |
| 154 | + if (res.code == 200 && res.data.list.length > 0) { | |
| 155 | + this.departmentList = res.data.list.map(item => ({ | |
| 156 | + id: item.id, | |
| 157 | + name: item.fullName, | |
| 158 | + })) | |
| 159 | + } else { | |
| 160 | + this.departmentList = [] | |
| 161 | + } | |
| 162 | + }) | |
| 163 | + }, | |
| 164 | + | |
| 165 | + // 获取库存列表 | |
| 166 | + getList() { | |
| 167 | + this.listLoading = true | |
| 168 | + getInventoryList(this.query).then(response => { | |
| 169 | + if (response.code === 200) { | |
| 170 | + this.list = response.data.list || [] | |
| 171 | + this.total = (response.data.pagination && response.data.pagination.total) || 0 | |
| 172 | + } else { | |
| 173 | + this.$message.error(response.msg || '获取库存列表失败') | |
| 174 | + } | |
| 175 | + this.listLoading = false | |
| 176 | + }).catch(() => { | |
| 177 | + this.listLoading = false | |
| 178 | + }) | |
| 179 | + }, | |
| 180 | + | |
| 181 | + // 搜索 | |
| 182 | + search() { | |
| 183 | + this.query.currentPage = 1 | |
| 184 | + this.getList() | |
| 185 | + }, | |
| 186 | + | |
| 187 | + // 重置 | |
| 188 | + reset() { | |
| 189 | + this.query = { | |
| 190 | + productName: '', | |
| 191 | + productCategory: '', | |
| 192 | + departmentId: '', | |
| 193 | + currentPage: 1, | |
| 194 | + pageSize: 20 | |
| 195 | + } | |
| 196 | + this.getList() | |
| 197 | + }, | |
| 198 | + | |
| 199 | + // 添加库存 | |
| 200 | + addInventoryHandle() { | |
| 201 | + this.inventoryFormVisible = true | |
| 202 | + this.$nextTick(() => { | |
| 203 | + this.$refs.InventoryForm.init() | |
| 204 | + }) | |
| 205 | + }, | |
| 206 | + | |
| 207 | + // 编辑库存 | |
| 208 | + editInventoryHandle() { | |
| 209 | + if (this.selectedIds.length === 0) { | |
| 210 | + this.$message.warning('请选择一个库存记录') | |
| 211 | + return | |
| 212 | + } | |
| 213 | + if (this.selectedIds.length > 1) { | |
| 214 | + this.$message.warning('请只选择一个库存记录') | |
| 215 | + return | |
| 216 | + } | |
| 217 | + | |
| 218 | + const selectedInventory = this.list.find(item => item.id === this.selectedIds[0]) | |
| 219 | + if (selectedInventory) { | |
| 220 | + this.inventoryFormVisible = true | |
| 221 | + this.$nextTick(() => { | |
| 222 | + this.$refs.InventoryForm.init(selectedInventory.id) | |
| 223 | + }) | |
| 224 | + } | |
| 225 | + }, | |
| 226 | + | |
| 227 | + // 编辑库存(从操作列) | |
| 228 | + editInventory(row) { | |
| 229 | + this.inventoryFormVisible = true | |
| 230 | + this.$nextTick(() => { | |
| 231 | + this.$refs.InventoryForm.init(row.id) | |
| 232 | + }) | |
| 233 | + }, | |
| 234 | + | |
| 235 | + // 查看库存详情 | |
| 236 | + viewInventoryInfo(row) { | |
| 237 | + this.inventoryInfoVisible = true | |
| 238 | + this.$nextTick(() => { | |
| 239 | + this.$refs.InventoryInfoDialog.init(row.id) | |
| 240 | + }) | |
| 241 | + }, | |
| 242 | + | |
| 243 | + // 使用记录管理 | |
| 244 | + usageRecordHandle() { | |
| 245 | + this.usageRecordVisible = true | |
| 246 | + this.$nextTick(() => { | |
| 247 | + this.$refs.UsageRecordDialog.init() | |
| 248 | + }) | |
| 249 | + }, | |
| 250 | + | |
| 251 | + // 查看使用记录 | |
| 252 | + viewUsageRecords(row) { | |
| 253 | + this.usageRecordVisible = true | |
| 254 | + this.$nextTick(() => { | |
| 255 | + this.$refs.UsageRecordDialog.init(row.id, row.productName) | |
| 256 | + }) | |
| 257 | + }, | |
| 258 | + | |
| 259 | + // 多选 | |
| 260 | + handleSelectionChange(selection) { | |
| 261 | + this.selectedIds = selection.map(item => item.id) | |
| 262 | + }, | |
| 263 | + | |
| 264 | + // 格式化金额 | |
| 265 | + formatMoney(value) { | |
| 266 | + if (value === null || value === undefined || value === '') { | |
| 267 | + return '0.00' | |
| 268 | + } | |
| 269 | + return Number(value).toFixed(2) | |
| 270 | + }, | |
| 271 | + | |
| 272 | + // 导出 | |
| 273 | + exportData() { | |
| 274 | + this.$message.info('导出功能开发中...') | |
| 275 | + } | |
| 276 | + } | |
| 277 | +} | |
| 278 | +</script> | |
| 279 | + | |
| 280 | +<style scoped> | |
| 281 | +.NCC-common-layout { | |
| 282 | + height: 100%; | |
| 283 | +} | |
| 284 | + | |
| 285 | +.text-danger { | |
| 286 | + color: #f56c6c; | |
| 287 | + font-weight: bold; | |
| 288 | +} | |
| 289 | +</style> | ... | ... |
antis-ncc-admin/src/views/lqInventory/index.vue
| 1 | 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> | |
| 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="productQuery.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="productQuery.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="productQuery.onShelfStatus" placeholder="上架状态" clearable> | |
| 20 | + <el-option label="上架" :value="1" /> | |
| 21 | + <el-option label="下架" :value="0" /> | |
| 22 | + </el-select> | |
| 23 | + </el-form-item> | |
| 24 | + </el-col> | |
| 25 | + <el-col :span="6"> | |
| 26 | + <el-form-item> | |
| 27 | + <el-button type="primary" icon="el-icon-search" @click="searchProduct()">查询</el-button> | |
| 28 | + <el-button icon="el-icon-refresh-right" @click="resetProduct()">重置</el-button> | |
| 29 | + </el-form-item> | |
| 30 | + </el-col> | |
| 31 | + </el-form> | |
| 32 | + </el-row> | |
| 33 | + <!-- 产品列表 --> | |
| 34 | + <div class="NCC-common-layout-main NCC-flex-main"> | |
| 35 | + <div class="NCC-common-head"> | |
| 36 | + <div> | |
| 37 | + <el-button type="primary" icon="el-icon-plus" @click="addProduct()">新增产品</el-button> | |
| 38 | + <el-button type="primary" icon="el-icon-plus" @click="addInventory()">添加库存</el-button> | |
| 39 | + <el-button type="primary" icon="el-icon-plus" @click="addUsage()">添加使用记录</el-button> | |
| 40 | + </div> | |
| 41 | + <div class="NCC-common-head-right"> | |
| 42 | + <el-tooltip effect="dark" content="刷新" placement="top"> | |
| 43 | + <el-link icon="icon-ym icon-ym-Refresh NCC-common-head-icon" :underline="false" | |
| 44 | + @click="initProductData()" /> | |
| 45 | + </el-tooltip> | |
| 46 | + <screenfull isContainer /> | |
| 47 | + </div> | |
| 48 | + </div> | |
| 49 | + <NCC-table v-loading="productLoading" :data="productList" | |
| 50 | + :header-cell-style="{ background: '#f5f7fa', color: '#606266' }"> | |
| 51 | + <!-- <el-table-column label="产品ID" align="center"> | |
| 52 | + <template slot-scope="scope"> | |
| 53 | + <div class="product-id-info"> | |
| 54 | + <i class="el-icon-postcard product-id-icon"></i> | |
| 55 | + <span class="text-nowrap">{{ scope.row.id || '无' }}</span> | |
| 56 | + </div> | |
| 57 | + </template> | |
| 58 | + </el-table-column> --> | |
| 59 | + <el-table-column label="产品名称" width="180" align="center" prop="productName"/> | |
| 60 | + <el-table-column label="价格" align="center"> | |
| 61 | + <template slot-scope="scope"> | |
| 62 | + <div class="price-info"> | |
| 63 | + <i class="el-icon-coin price-icon"></i> | |
| 64 | + <span class="text-nowrap">¥{{ formatMoney(scope.row.price) }}</span> | |
| 65 | + </div> | |
| 66 | + </template> | |
| 67 | + </el-table-column> | |
| 68 | + <el-table-column label="库存" align="center" prop="currentInventory"/> | |
| 69 | + <el-table-column label="产品类别" align="center" prop="productCategory"/> | |
| 70 | + <el-table-column label="归属部门" align="center" prop="departmentName"> | |
| 71 | + <template slot-scope="scope"> | |
| 72 | + <div class="department-info"> | |
| 73 | + <span class="text-nowrap">{{ scope.row.departmentName || '无' }}</span> | |
| 74 | + </div> | |
| 75 | + </template> | |
| 76 | + </el-table-column> | |
| 34 | 77 | |
| 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" /> | |
| 60 | - <el-table-column prop="price" label="单价" align="left"> | |
| 61 | - <template slot-scope="scope"> | |
| 62 | - {{ formatMoney(scope.row.price) }} | |
| 63 | - </template> | |
| 64 | - </el-table-column> | |
| 65 | - <el-table-column prop="quantity" label="库存数量" align="left" > | |
| 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="left" /> | |
| 71 | - <el-table-column prop="totalValue" label="总价值" align="left"> | |
| 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" /> --> | |
| 77 | - <el-table-column prop="createTime" label="创建时间" align="left" :formatter="ncc.tableDateFormat"> | |
| 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> | |
| 78 | + <el-table-column label="标准单位" align="center" prop="standardUnit"/> | |
| 79 | + <el-table-column label="上架状态" align="center" prop="onShelfStatus"> | |
| 80 | + <template slot-scope="scope"> | |
| 81 | + <div class="shelf-status-info"> | |
| 82 | + <el-tag :type="scope.row.onShelfStatus === 1 ? 'success' : 'info'" size="small"> | |
| 83 | + {{ scope.row.onShelfStatus === 1 ? '上架' : '下架' }} | |
| 84 | + </el-tag> | |
| 85 | + </div> | |
| 86 | + </template> | |
| 87 | + </el-table-column> | |
| 88 | + <el-table-column label="统计分类" align="center" prop="statisticsCategory"/> | |
| 89 | + <el-table-column label="归属仓库" align="center" prop="warehouse"> | |
| 90 | + <template slot-scope="scope"> | |
| 91 | + <div class="warehouse-info"> | |
| 92 | + <i class="el-icon-office-building warehouse-icon"></i> | |
| 93 | + <span class="text-nowrap">{{ scope.row.warehouse || '无' }}</span> | |
| 94 | + </div> | |
| 95 | + </template> | |
| 96 | + </el-table-column> | |
| 97 | + <el-table-column label="供应商名称" align="center" prop="supplierName"/> | |
| 98 | + <!-- <el-table-column label="合同签订日期" align="center" prop="contractSignDate"/> | |
| 99 | + <el-table-column label="合同结束日期" align="center" prop="contractEndDate"/> | |
| 100 | + <el-table-column label="备注" align="center" prop="remark"/> --> | |
| 101 | + <el-table-column label="操作" width="280" align="left" fixed="right"> | |
| 102 | + <template slot-scope="scope"> | |
| 103 | + <div class="action-buttons"> | |
| 104 | + <el-button v-if="scope.row.onShelfStatus === 1" type="text" icon="el-icon-view" @click="viewDetail(scope.row)" | |
| 105 | + class="view-btn"> | |
| 106 | + 查看详情 | |
| 107 | + </el-button> | |
| 108 | + <el-button type="text" @click="toggleShelf(scope.row)"> | |
| 109 | + {{ scope.row.onShelfStatus === 1 ? '下架' : '上架' }} | |
| 110 | + </el-button> | |
| 111 | + <el-button v-if="scope.row.onShelfStatus === 1" type="text" icon="el-icon-edit" @click="editProduct(scope.row)" | |
| 112 | + class="edit-btn"> | |
| 113 | + 编辑 | |
| 114 | + </el-button> | |
| 115 | + </div> | |
| 116 | + </template> | |
| 117 | + </el-table-column> | |
| 118 | + </NCC-table> | |
| 119 | + <pagination :total="productTotal" :page.sync="productQuery.currentPage" | |
| 120 | + :limit.sync="productQuery.pageSize" @pagination="initProductData" /> | |
| 121 | + </div> | |
| 122 | + </div> | |
| 123 | + <!-- 产品表单弹窗 --> | |
| 124 | + <ProductForm v-if="productFormVisible" ref="ProductForm" @refresh="refreshProduct" /> | |
| 125 | + <!-- 产品详情弹窗(库存和使用记录) --> | |
| 126 | + <ProductDetailDialog v-if="detailDialogVisible" ref="ProductDetailDialog" @refresh="refreshProduct" /> | |
| 127 | + <!-- 添加使用记录弹窗(多产品) --> | |
| 128 | + <UsageMultiForm v-if="usageMultiFormVisible" ref="UsageMultiForm" @refresh="refreshProduct" /> | |
| 129 | + <!-- 添加库存弹窗 --> | |
| 130 | + <InventoryForm v-if="inventoryFormVisible" ref="InventoryForm" @refresh="refreshProduct" /> | |
| 131 | + </div> | |
| 106 | 132 | </template> |
| 107 | 133 | |
| 108 | 134 | <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 | 135 | import request from '@/utils/request' |
| 136 | +import ProductForm from './product-form.vue' | |
| 137 | +import ProductDetailDialog from './product-detail-dialog.vue' | |
| 138 | +import UsageMultiForm from './usage-multi-form.vue' | |
| 139 | +import InventoryForm from './inventory-form.vue' | |
| 140 | + | |
| 116 | 141 | 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 | - request({ | |
| 151 | - url: `/api/permission/Organize/96240625-934F-490B-8AA6-0BC775B18468/Department`, | |
| 142 | + components: { ProductForm, ProductDetailDialog, UsageMultiForm, InventoryForm }, | |
| 143 | + data() { | |
| 144 | + return { | |
| 145 | + // 产品相关 | |
| 146 | + productList: [], | |
| 147 | + productLoading: false, | |
| 148 | + productTotal: 0, | |
| 149 | + productQuery: { | |
| 150 | + currentPage: 1, | |
| 151 | + pageSize: 20, | |
| 152 | + productName: undefined, | |
| 153 | + productCategory: undefined, | |
| 154 | + onShelfStatus: undefined | |
| 155 | + }, | |
| 156 | + productFormVisible: false, | |
| 157 | + detailDialogVisible: false, | |
| 158 | + usageMultiFormVisible: false, | |
| 159 | + inventoryFormVisible: false | |
| 160 | + } | |
| 161 | + }, | |
| 162 | + created() { | |
| 163 | + this.initProductData() | |
| 164 | + }, | |
| 165 | + methods: { | |
| 166 | + // 产品相关方法 | |
| 167 | + initProductData() { | |
| 168 | + this.productLoading = true | |
| 169 | + let query = { ...this.productQuery } | |
| 170 | + // 移除空值 | |
| 171 | + Object.keys(query).forEach(key => { | |
| 172 | + if (query[key] === undefined || query[key] === null || query[key] === '') { | |
| 173 | + delete query[key] | |
| 174 | + } | |
| 175 | + }) | |
| 176 | + request({ | |
| 177 | + url: '/api/Extend/LqProduct/GetList', | |
| 152 | 178 | method: 'GET', |
| 153 | - }).then((res) => { | |
| 154 | - if (res.code == 200 && res.data.list.length > 0) { | |
| 155 | - this.departmentList = res.data.list.map(item => ({ | |
| 156 | - id: item.id, | |
| 157 | - name: item.fullName, | |
| 158 | - })) | |
| 159 | - } else { | |
| 160 | - this.departmentList = [] | |
| 161 | - } | |
| 162 | - }) | |
| 163 | - }, | |
| 179 | + data: query | |
| 180 | + }).then(res => { | |
| 181 | + if (res.code == 200 && res.data) { | |
| 182 | + this.productList = res.data.list || [] | |
| 183 | + this.productTotal = res.data.pagination ? res.data.pagination.total : 0 | |
| 184 | + } else { | |
| 185 | + this.productList = [] | |
| 186 | + this.productTotal = 0 | |
| 187 | + } | |
| 188 | + this.productLoading = false | |
| 189 | + }).catch(() => { | |
| 190 | + this.productLoading = false | |
| 191 | + this.productList = [] | |
| 192 | + this.productTotal = 0 | |
| 193 | + }) | |
| 194 | + }, | |
| 195 | + searchProduct() { | |
| 196 | + this.productQuery.currentPage = 1 | |
| 197 | + this.initProductData() | |
| 198 | + }, | |
| 199 | + resetProduct() { | |
| 200 | + this.productQuery = { | |
| 201 | + currentPage: 1, | |
| 202 | + pageSize: 20, | |
| 203 | + productName: undefined, | |
| 204 | + productCategory: undefined, | |
| 205 | + onShelfStatus: undefined | |
| 206 | + } | |
| 207 | + this.initProductData() | |
| 208 | + }, | |
| 209 | + addProduct() { | |
| 210 | + this.productFormVisible = true | |
| 211 | + this.$nextTick(() => { | |
| 212 | + this.$refs.ProductForm.init() | |
| 213 | + }) | |
| 214 | + }, | |
| 215 | + editProduct(row) { | |
| 216 | + this.productFormVisible = true | |
| 217 | + this.$nextTick(() => { | |
| 218 | + this.$refs.ProductForm.init(row.id) | |
| 219 | + }) | |
| 220 | + }, | |
| 221 | + viewDetail(row) { | |
| 222 | + this.detailDialogVisible = true | |
| 223 | + this.$nextTick(() => { | |
| 224 | + this.$refs.ProductDetailDialog.init(row.id, row) | |
| 225 | + }) | |
| 226 | + }, | |
| 227 | + toggleShelf(row) { | |
| 228 | + const status = row.onShelfStatus === 1 ? 0 : 1 | |
| 229 | + const statusText = status === 1 ? '上架' : '下架' | |
| 230 | + this.$confirm(`确定要${statusText}该产品吗?`, '提示', { | |
| 231 | + type: 'warning' | |
| 232 | + }).then(() => { | |
| 233 | + request({ | |
| 234 | + url: '/api/Extend/LqProduct/ToggleShelf', | |
| 235 | + method: 'PUT', | |
| 236 | + data: { | |
| 237 | + productId: row.id, | |
| 238 | + onShelfStatus: status | |
| 239 | + } | |
| 240 | + }).then(res => { | |
| 241 | + this.$message({ | |
| 242 | + type: 'success', | |
| 243 | + message: res.msg || `${statusText}成功`, | |
| 244 | + onClose: () => { | |
| 245 | + this.initProductData() | |
| 246 | + } | |
| 247 | + }) | |
| 248 | + }) | |
| 249 | + }).catch(() => { }) | |
| 250 | + }, | |
| 251 | + refreshProduct() { | |
| 252 | + this.productFormVisible = false | |
| 253 | + this.usageMultiFormVisible = false | |
| 254 | + this.inventoryFormVisible = false | |
| 255 | + this.initProductData() | |
| 256 | + }, | |
| 257 | + addInventory() { | |
| 258 | + this.inventoryFormVisible = true | |
| 259 | + this.$nextTick(() => { | |
| 260 | + // 从列表页调用,不传产品ID,只显示上架产品 | |
| 261 | + this.$refs.InventoryForm.init() | |
| 262 | + }) | |
| 263 | + }, | |
| 264 | + addUsage() { | |
| 265 | + this.usageMultiFormVisible = true | |
| 266 | + this.$nextTick(() => { | |
| 267 | + this.$refs.UsageMultiForm.init() | |
| 268 | + }) | |
| 269 | + }, | |
| 270 | + // 工具方法 | |
| 271 | + formatMoney(amount) { | |
| 272 | + if (!amount && amount !== 0) return '0.00' | |
| 273 | + return Number(amount).toFixed(2) | |
| 274 | + } | |
| 275 | + } | |
| 276 | +} | |
| 277 | +</script> | |
| 164 | 278 | |
| 165 | - // 获取库存列表 | |
| 166 | - getList() { | |
| 167 | - this.listLoading = true | |
| 168 | - getInventoryList(this.query).then(response => { | |
| 169 | - if (response.code === 200) { | |
| 170 | - this.list = response.data.list || [] | |
| 171 | - this.total = (response.data.pagination && response.data.pagination.total) || 0 | |
| 172 | - } else { | |
| 173 | - this.$message.error(response.msg || '获取库存列表失败') | |
| 174 | - } | |
| 175 | - this.listLoading = false | |
| 176 | - }).catch(() => { | |
| 177 | - this.listLoading = false | |
| 178 | - }) | |
| 179 | - }, | |
| 279 | +<style lang="scss" scoped> | |
| 280 | +// 通用文本不换行样式 | |
| 281 | +.text-nowrap { | |
| 282 | + white-space: nowrap; | |
| 283 | + overflow: hidden; | |
| 284 | + text-overflow: ellipsis; | |
| 285 | + max-width: 100%; | |
| 286 | +} | |
| 180 | 287 | |
| 181 | - // 搜索 | |
| 182 | - search() { | |
| 183 | - this.query.currentPage = 1 | |
| 184 | - this.getList() | |
| 185 | - }, | |
| 288 | +// 产品相关样式 | |
| 289 | +.product-id-info, | |
| 290 | +.product-name-info, | |
| 291 | +.price-info, | |
| 292 | +.category-info, | |
| 293 | +.unit-info, | |
| 294 | +.shelf-status-info, | |
| 295 | +.statistics-info, | |
| 296 | +.warehouse-info, | |
| 297 | +.supplier-info { | |
| 298 | + display: flex; | |
| 299 | + align-items: center; | |
| 300 | + justify-content: center; | |
| 301 | + gap: 6px; | |
| 302 | +} | |
| 303 | + | |
| 304 | +.product-id-icon, | |
| 305 | +.product-name-icon { | |
| 306 | + color: #409EFF; | |
| 307 | + font-size: 16px; | |
| 308 | +} | |
| 186 | 309 | |
| 187 | - // 重置 | |
| 188 | - reset() { | |
| 189 | - this.query = { | |
| 190 | - productName: '', | |
| 191 | - productCategory: '', | |
| 192 | - departmentId: '', | |
| 193 | - currentPage: 1, | |
| 194 | - pageSize: 20 | |
| 195 | - } | |
| 196 | - this.getList() | |
| 197 | - }, | |
| 310 | +.price-icon { | |
| 311 | + color: #67C23A; | |
| 312 | + font-size: 16px; | |
| 313 | +} | |
| 198 | 314 | |
| 199 | - // 添加库存 | |
| 200 | - addInventoryHandle() { | |
| 201 | - this.inventoryFormVisible = true | |
| 202 | - this.$nextTick(() => { | |
| 203 | - this.$refs.InventoryForm.init() | |
| 204 | - }) | |
| 205 | - }, | |
| 315 | +.category-icon, | |
| 316 | +.unit-icon, | |
| 317 | +.statistics-icon { | |
| 318 | + color: #909399; | |
| 319 | + font-size: 16px; | |
| 320 | +} | |
| 206 | 321 | |
| 207 | - // 编辑库存 | |
| 208 | - editInventoryHandle() { | |
| 209 | - if (this.selectedIds.length === 0) { | |
| 210 | - this.$message.warning('请选择一个库存记录') | |
| 211 | - return | |
| 212 | - } | |
| 213 | - if (this.selectedIds.length > 1) { | |
| 214 | - this.$message.warning('请只选择一个库存记录') | |
| 215 | - return | |
| 216 | - } | |
| 322 | +.shelf-status-icon { | |
| 323 | + font-size: 16px; | |
| 217 | 324 | |
| 218 | - const selectedInventory = this.list.find(item => item.id === this.selectedIds[0]) | |
| 219 | - if (selectedInventory) { | |
| 220 | - this.inventoryFormVisible = true | |
| 221 | - this.$nextTick(() => { | |
| 222 | - this.$refs.InventoryForm.init(selectedInventory.id) | |
| 223 | - }) | |
| 224 | - } | |
| 225 | - }, | |
| 325 | + &.status-on { | |
| 326 | + color: #67C23A; | |
| 327 | + } | |
| 226 | 328 | |
| 227 | - // 编辑库存(从操作列) | |
| 228 | - editInventory(row) { | |
| 229 | - this.inventoryFormVisible = true | |
| 230 | - this.$nextTick(() => { | |
| 231 | - this.$refs.InventoryForm.init(row.id) | |
| 232 | - }) | |
| 233 | - }, | |
| 329 | + &.status-off { | |
| 330 | + color: #909399; | |
| 331 | + } | |
| 332 | +} | |
| 234 | 333 | |
| 235 | - // 查看库存详情 | |
| 236 | - viewInventoryInfo(row) { | |
| 237 | - this.inventoryInfoVisible = true | |
| 238 | - this.$nextTick(() => { | |
| 239 | - this.$refs.InventoryInfoDialog.init(row.id) | |
| 240 | - }) | |
| 241 | - }, | |
| 334 | +.warehouse-icon, | |
| 335 | +.supplier-icon { | |
| 336 | + color: #409EFF; | |
| 337 | + font-size: 16px; | |
| 338 | +} | |
| 242 | 339 | |
| 243 | - // 使用记录管理 | |
| 244 | - usageRecordHandle() { | |
| 245 | - this.usageRecordVisible = true | |
| 246 | - this.$nextTick(() => { | |
| 247 | - this.$refs.UsageRecordDialog.init() | |
| 248 | - }) | |
| 249 | - }, | |
| 340 | +// 操作按钮样式 | |
| 341 | +.action-buttons { | |
| 342 | + display: flex; | |
| 343 | + align-items: center; | |
| 344 | + gap: 8px; | |
| 250 | 345 | |
| 251 | - // 查看使用记录 | |
| 252 | - viewUsageRecords(row) { | |
| 253 | - this.usageRecordVisible = true | |
| 254 | - this.$nextTick(() => { | |
| 255 | - this.$refs.UsageRecordDialog.init(row.id, row.productName) | |
| 256 | - }) | |
| 257 | - }, | |
| 346 | + .view-btn { | |
| 347 | + color: #409EFF; | |
| 258 | 348 | |
| 259 | - // 多选 | |
| 260 | - handleSelectionChange(selection) { | |
| 261 | - this.selectedIds = selection.map(item => item.id) | |
| 262 | - }, | |
| 349 | + &:hover { | |
| 350 | + color: #66b1ff; | |
| 351 | + } | |
| 352 | + } | |
| 263 | 353 | |
| 264 | - // 格式化金额 | |
| 265 | - formatMoney(value) { | |
| 266 | - if (value === null || value === undefined || value === '') { | |
| 267 | - return '0.00' | |
| 268 | - } | |
| 269 | - return Number(value).toFixed(2) | |
| 270 | - }, | |
| 354 | + .edit-btn { | |
| 355 | + color: #409EFF; | |
| 271 | 356 | |
| 272 | - // 导出 | |
| 273 | - exportData() { | |
| 274 | - this.$message.info('导出功能开发中...') | |
| 275 | - } | |
| 276 | - } | |
| 357 | + &:hover { | |
| 358 | + color: #66b1ff; | |
| 359 | + } | |
| 360 | + } | |
| 277 | 361 | } |
| 278 | -</script> | |
| 279 | 362 | |
| 280 | -<style scoped> | |
| 281 | -.NCC-common-layout { | |
| 282 | - height: 100%; | |
| 363 | +// 表格行悬停效果 | |
| 364 | +::v-deep .el-table__row:hover { | |
| 365 | + background-color: #f5f7fa; | |
| 283 | 366 | } |
| 284 | 367 | |
| 285 | -.text-danger { | |
| 286 | - color: #f56c6c; | |
| 287 | - font-weight: bold; | |
| 368 | +// 表格头部样式 | |
| 369 | +::v-deep .el-table__header-wrapper { | |
| 370 | + .el-table__header { | |
| 371 | + th { | |
| 372 | + background-color: #f5f7fa; | |
| 373 | + color: #606266; | |
| 374 | + font-weight: 600; | |
| 375 | + } | |
| 376 | + } | |
| 288 | 377 | } |
| 289 | 378 | </style> | ... | ... |
antis-ncc-admin/src/views/lqInventory/inventory-form.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog title="添加库存" :close-on-click-modal="false" :visible.sync="visible" class="NCC-dialog NCC-dialog_center" | |
| 3 | + lock-scroll width="800px"> | |
| 4 | + <el-form ref="elForm" :model="dataForm" size="small" label-width="120px" label-position="right" :rules="rules"> | |
| 5 | + <el-row :gutter="15"> | |
| 6 | + <el-col :span="12"> | |
| 7 | + <el-form-item label="产品" prop="productId"> | |
| 8 | + <el-select v-model="dataForm.productId" placeholder="请选择产品" clearable filterable | |
| 9 | + :style='{"width":"100%"}' :disabled="!!defaultProductId" @change="onProductChange"> | |
| 10 | + <el-option v-for="item in productOptions" :key="item.id" :label="item.productName" | |
| 11 | + :value="item.id" /> | |
| 12 | + </el-select> | |
| 13 | + </el-form-item> | |
| 14 | + </el-col> | |
| 15 | + <el-col :span="12"> | |
| 16 | + <el-form-item label="数量" prop="quantity"> | |
| 17 | + <el-input-number v-model="dataForm.quantity" placeholder="请输入数量" :min="1" :precision="0" | |
| 18 | + :style='{"width":"100%"}' /> | |
| 19 | + </el-form-item> | |
| 20 | + </el-col> | |
| 21 | + <el-col :span="12"> | |
| 22 | + <el-form-item label="入库时间" prop="stockInTime"> | |
| 23 | + <el-date-picker v-model="dataForm.stockInTime" type="date" placeholder="请选择入库时间" | |
| 24 | + value-format="yyyy-MM-dd" format="yyyy-MM-dd" :style='{"width":"100%"}' /> | |
| 25 | + </el-form-item> | |
| 26 | + </el-col> | |
| 27 | + <el-col :span="12"> | |
| 28 | + <el-form-item label="生产日期" prop="productionDate"> | |
| 29 | + <el-date-picker v-model="dataForm.productionDate" type="date" placeholder="请选择生产日期" | |
| 30 | + value-format="yyyy-MM-dd" format="yyyy-MM-dd" :style='{"width":"100%"}' /> | |
| 31 | + </el-form-item> | |
| 32 | + </el-col> | |
| 33 | + <el-col :span="12"> | |
| 34 | + <el-form-item label="保质期(天)" prop="shelfLife"> | |
| 35 | + <el-input-number v-model="dataForm.shelfLife" placeholder="请输入保质期" :min="1" :precision="0" | |
| 36 | + :style='{"width":"100%"}' /> | |
| 37 | + </el-form-item> | |
| 38 | + </el-col> | |
| 39 | + <el-col :span="12"> | |
| 40 | + <el-form-item label="批次号" prop="batchNumber"> | |
| 41 | + <el-input v-model="dataForm.batchNumber" placeholder="请输入批次号" clearable | |
| 42 | + :style='{"width":"100%"}' /> | |
| 43 | + </el-form-item> | |
| 44 | + </el-col> | |
| 45 | + </el-row> | |
| 46 | + </el-form> | |
| 47 | + <span slot="footer" class="dialog-footer"> | |
| 48 | + <el-button @click="visible = false">取 消</el-button> | |
| 49 | + <el-button type="primary" @click="dataFormSubmit()" :loading="btnLoading">确 定</el-button> | |
| 50 | + </span> | |
| 51 | + </el-dialog> | |
| 52 | +</template> | |
| 53 | + | |
| 54 | +<script> | |
| 55 | +import request from '@/utils/request' | |
| 56 | + | |
| 57 | +export default { | |
| 58 | + data() { | |
| 59 | + return { | |
| 60 | + visible: false, | |
| 61 | + btnLoading: false, | |
| 62 | + defaultProductId: '', | |
| 63 | + dataForm: { | |
| 64 | + productId: '', | |
| 65 | + quantity: 1, | |
| 66 | + stockInTime: '', | |
| 67 | + productionDate: '', | |
| 68 | + shelfLife: 365, | |
| 69 | + batchNumber: '' | |
| 70 | + }, | |
| 71 | + productOptions: [], | |
| 72 | + rules: { | |
| 73 | + productId: [ | |
| 74 | + { required: true, message: '请选择产品', trigger: 'change' } | |
| 75 | + ], | |
| 76 | + quantity: [ | |
| 77 | + { required: true, message: '请输入数量', trigger: 'blur' } | |
| 78 | + ], | |
| 79 | + stockInTime: [ | |
| 80 | + { required: true, message: '请选择入库时间', trigger: 'change' } | |
| 81 | + ] | |
| 82 | + } | |
| 83 | + } | |
| 84 | + }, | |
| 85 | + methods: { | |
| 86 | + init(productId) { | |
| 87 | + this.visible = true | |
| 88 | + this.defaultProductId = productId || '' | |
| 89 | + this.dataForm = { | |
| 90 | + productId: productId || '', | |
| 91 | + quantity: 1, | |
| 92 | + stockInTime: '', | |
| 93 | + productionDate: '', | |
| 94 | + shelfLife: 365, | |
| 95 | + batchNumber: '' | |
| 96 | + } | |
| 97 | + this.initProductOptions() | |
| 98 | + }, | |
| 99 | + initProductOptions() { | |
| 100 | + // 如果传入了产品ID,只显示该产品;否则只显示上架的产品 | |
| 101 | + const query = { | |
| 102 | + currentPage: 1, | |
| 103 | + pageSize: 1000 | |
| 104 | + } | |
| 105 | + // 如果传入了产品ID,则查询该产品;否则只查询上架的产品 | |
| 106 | + if (this.defaultProductId) { | |
| 107 | + query.id = this.defaultProductId | |
| 108 | + } else { | |
| 109 | + query.onShelfStatus = 1 // 只获取上架的产品 | |
| 110 | + } | |
| 111 | + request({ | |
| 112 | + url: '/api/Extend/LqProduct/GetList', | |
| 113 | + method: 'GET', | |
| 114 | + data: query | |
| 115 | + }).then(res => { | |
| 116 | + if (res.code == 200 && res.data && res.data.list) { | |
| 117 | + if (this.defaultProductId) { | |
| 118 | + // 如果传入了产品ID,只显示该产品 | |
| 119 | + this.productOptions = res.data.list | |
| 120 | + } else { | |
| 121 | + // 只显示上架的产品 | |
| 122 | + this.productOptions = res.data.list.filter(product => product.onShelfStatus === 1) | |
| 123 | + } | |
| 124 | + } | |
| 125 | + }).catch(() => { | |
| 126 | + this.productOptions = [] | |
| 127 | + }) | |
| 128 | + }, | |
| 129 | + onProductChange() { | |
| 130 | + // 产品选择变化时的处理 | |
| 131 | + }, | |
| 132 | + dataFormSubmit() { | |
| 133 | + this.$refs.elForm.validate((valid) => { | |
| 134 | + if (!valid) { | |
| 135 | + return false | |
| 136 | + } | |
| 137 | + this.btnLoading = true | |
| 138 | + request({ | |
| 139 | + url: '/api/Extend/LqInventory/Create', | |
| 140 | + method: 'POST', | |
| 141 | + data: { | |
| 142 | + productId: this.dataForm.productId, | |
| 143 | + quantity: this.dataForm.quantity, | |
| 144 | + stockInTime: this.dataForm.stockInTime, | |
| 145 | + productionDate: this.dataForm.productionDate, | |
| 146 | + shelfLife: this.dataForm.shelfLife, | |
| 147 | + batchNumber: this.dataForm.batchNumber | |
| 148 | + } | |
| 149 | + }).then(res => { | |
| 150 | + this.btnLoading = false | |
| 151 | + this.$message({ | |
| 152 | + type: 'success', | |
| 153 | + message: res.msg || '添加成功', | |
| 154 | + onClose: () => { | |
| 155 | + this.visible = false | |
| 156 | + this.$emit('refresh') | |
| 157 | + } | |
| 158 | + }) | |
| 159 | + }).catch(() => { | |
| 160 | + this.btnLoading = false | |
| 161 | + }) | |
| 162 | + }) | |
| 163 | + } | |
| 164 | + } | |
| 165 | +} | |
| 166 | +</script> | |
| 167 | + | |
| 168 | +<style lang="scss" scoped> | |
| 169 | +</style> | |
| 170 | + | ... | ... |
antis-ncc-admin/src/views/lqInventory/product-detail-dialog.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div> | |
| 3 | + <el-dialog title="产品详情" :close-on-click-modal="false" :visible.sync="visible" class="NCC-dialog NCC-dialog_center" | |
| 4 | + lock-scroll width="1200px"> | |
| 5 | + <div class="detail-content"> | |
| 6 | + <!-- 产品基本信息 --> | |
| 7 | + <el-card class="info-card" shadow="hover" v-if="productInfo"> | |
| 8 | + <div slot="header" class="card-header"> | |
| 9 | + <i class="el-icon-goods"></i> | |
| 10 | + <span class="card-title">产品信息</span> | |
| 11 | + </div> | |
| 12 | + <el-row :gutter="20"> | |
| 13 | + <el-col :span="8"> | |
| 14 | + <div class="info-item"> | |
| 15 | + <label class="info-label">产品名称:</label> | |
| 16 | + <span class="info-value">{{ productInfo.productName || '无' }}</span> | |
| 17 | + </div> | |
| 18 | + </el-col> | |
| 19 | + <el-col :span="8"> | |
| 20 | + <div class="info-item"> | |
| 21 | + <label class="info-label">价格:</label> | |
| 22 | + <span class="info-value">¥{{ formatMoney(productInfo.price) }}</span> | |
| 23 | + </div> | |
| 24 | + </el-col> | |
| 25 | + <el-col :span="8"> | |
| 26 | + <div class="info-item"> | |
| 27 | + <label class="info-label">库存:</label> | |
| 28 | + <span class="info-value">{{ productInfo.currentInventory !== undefined ? productInfo.currentInventory : '无' }}</span> | |
| 29 | + </div> | |
| 30 | + </el-col> | |
| 31 | + </el-row> | |
| 32 | + <el-row :gutter="20"> | |
| 33 | + <el-col :span="8"> | |
| 34 | + <div class="info-item"> | |
| 35 | + <label class="info-label">产品类别:</label> | |
| 36 | + <span class="info-value">{{ productInfo.productCategory || '无' }}</span> | |
| 37 | + </div> | |
| 38 | + </el-col> | |
| 39 | + <el-col :span="8"> | |
| 40 | + <div class="info-item"> | |
| 41 | + <label class="info-label">归属部门:</label> | |
| 42 | + <span class="info-value">{{ productInfo.departmentName || '无' }}</span> | |
| 43 | + </div> | |
| 44 | + </el-col> | |
| 45 | + <el-col :span="8"> | |
| 46 | + <div class="info-item"> | |
| 47 | + <label class="info-label">标准单位:</label> | |
| 48 | + <span class="info-value">{{ productInfo.standardUnit || '无' }}</span> | |
| 49 | + </div> | |
| 50 | + </el-col> | |
| 51 | + </el-row> | |
| 52 | + <el-row :gutter="20"> | |
| 53 | + <el-col :span="8"> | |
| 54 | + <div class="info-item"> | |
| 55 | + <label class="info-label">上架状态:</label> | |
| 56 | + <el-tag :type="productInfo.onShelfStatus === 1 ? 'success' : 'info'" size="small"> | |
| 57 | + {{ productInfo.onShelfStatus === 1 ? '上架' : '下架' }} | |
| 58 | + </el-tag> | |
| 59 | + </div> | |
| 60 | + </el-col> | |
| 61 | + <el-col :span="8"> | |
| 62 | + <div class="info-item"> | |
| 63 | + <label class="info-label">统计分类:</label> | |
| 64 | + <span class="info-value">{{ productInfo.statisticsCategory || '无' }}</span> | |
| 65 | + </div> | |
| 66 | + </el-col> | |
| 67 | + <el-col :span="8"> | |
| 68 | + <div class="info-item"> | |
| 69 | + <label class="info-label">归属仓库:</label> | |
| 70 | + <span class="info-value">{{ productInfo.warehouse || '无' }}</span> | |
| 71 | + </div> | |
| 72 | + </el-col> | |
| 73 | + </el-row> | |
| 74 | + <el-row :gutter="20"> | |
| 75 | + <el-col :span="8"> | |
| 76 | + <div class="info-item"> | |
| 77 | + <label class="info-label">供应商名称:</label> | |
| 78 | + <span class="info-value">{{ productInfo.supplierName || '无' }}</span> | |
| 79 | + </div> | |
| 80 | + </el-col> | |
| 81 | + <el-col :span="8"> | |
| 82 | + <div class="info-item"> | |
| 83 | + <label class="info-label">合同签订日期:</label> | |
| 84 | + <span class="info-value">{{ formatDate(productInfo.contractSignDate) || '无' }}</span> | |
| 85 | + </div> | |
| 86 | + </el-col> | |
| 87 | + <el-col :span="8"> | |
| 88 | + <div class="info-item"> | |
| 89 | + <label class="info-label">合同结束日期:</label> | |
| 90 | + <span class="info-value">{{ formatDate(productInfo.contractEndDate) || '无' }}</span> | |
| 91 | + </div> | |
| 92 | + </el-col> | |
| 93 | + </el-row> | |
| 94 | + <el-row :gutter="20"> | |
| 95 | + <el-col :span="24"> | |
| 96 | + <div class="info-item"> | |
| 97 | + <label class="info-label">备注:</label> | |
| 98 | + <span class="info-value">{{ productInfo.remark || '无' }}</span> | |
| 99 | + </div> | |
| 100 | + </el-col> | |
| 101 | + </el-row> | |
| 102 | + </el-card> | |
| 103 | + | |
| 104 | + <!-- Tab标签页:库存和使用记录 --> | |
| 105 | + <el-tabs v-model="activeTab" class="detail-tabs"> | |
| 106 | + <!-- 库存管理Tab --> | |
| 107 | + <el-tab-pane label="库存管理" name="inventory"> | |
| 108 | + <div class="tab-content"> | |
| 109 | + <div class="tab-header"> | |
| 110 | + <el-button type="primary" icon="el-icon-plus" size="small" @click="addInventory()">添加库存</el-button> | |
| 111 | + </div> | |
| 112 | + <NCC-table v-loading="inventoryLoading" :data="inventoryList" has-c | |
| 113 | + :header-cell-style="{ background: '#f5f7fa', color: '#606266' }"> | |
| 114 | + <el-table-column label="库存ID" align="center"> | |
| 115 | + <template slot-scope="scope"> | |
| 116 | + <div class="inventory-id-info"> | |
| 117 | + <i class="el-icon-postcard inventory-id-icon"></i> | |
| 118 | + <span class="text-nowrap">{{ scope.row.id || '无' }}</span> | |
| 119 | + </div> | |
| 120 | + </template> | |
| 121 | + </el-table-column> | |
| 122 | + <el-table-column label="数量" align="center"> | |
| 123 | + <template slot-scope="scope"> | |
| 124 | + <div class="quantity-info"> | |
| 125 | + <i class="el-icon-s-data quantity-icon"></i> | |
| 126 | + <span class="text-nowrap">{{ scope.row.quantity || 0 }}</span> | |
| 127 | + </div> | |
| 128 | + </template> | |
| 129 | + </el-table-column> | |
| 130 | + <el-table-column label="入库时间" align="center"> | |
| 131 | + <template slot-scope="scope"> | |
| 132 | + <div class="stock-time-info"> | |
| 133 | + <i class="el-icon-time stock-time-icon"></i> | |
| 134 | + <span class="text-nowrap">{{ formatDate(scope.row.stockInTime) || '无' }}</span> | |
| 135 | + </div> | |
| 136 | + </template> | |
| 137 | + </el-table-column> | |
| 138 | + <el-table-column label="生产日期" align="center"> | |
| 139 | + <template slot-scope="scope"> | |
| 140 | + <div class="production-date-info"> | |
| 141 | + <i class="el-icon-date production-date-icon"></i> | |
| 142 | + <span class="text-nowrap">{{ formatDate(scope.row.productionDate) || '无' }}</span> | |
| 143 | + </div> | |
| 144 | + </template> | |
| 145 | + </el-table-column> | |
| 146 | + <el-table-column label="保质期(天)" align="center"> | |
| 147 | + <template slot-scope="scope"> | |
| 148 | + <div class="shelf-life-info"> | |
| 149 | + <i class="el-icon-timer shelf-life-icon"></i> | |
| 150 | + <span class="text-nowrap">{{ scope.row.shelfLife || '无' }}</span> | |
| 151 | + </div> | |
| 152 | + </template> | |
| 153 | + </el-table-column> | |
| 154 | + <el-table-column label="批次号" align="center"> | |
| 155 | + <template slot-scope="scope"> | |
| 156 | + <div class="batch-number-info"> | |
| 157 | + <i class="el-icon-document batch-number-icon"></i> | |
| 158 | + <span class="text-nowrap">{{ scope.row.batchNumber || '无' }}</span> | |
| 159 | + </div> | |
| 160 | + </template> | |
| 161 | + </el-table-column> | |
| 162 | + <!-- isEffective 是否有效 --> | |
| 163 | + <el-table-column label="是否有效" align="center"> | |
| 164 | + <template slot-scope="scope"> | |
| 165 | + <el-tag :type="scope.row.isEffective === 1 ? 'success' : 'info'" size="small"> | |
| 166 | + {{ scope.row.isEffective === 1 ? '有效' : '无效' }} | |
| 167 | + </el-tag> | |
| 168 | + </template> | |
| 169 | + </el-table-column> | |
| 170 | + <el-table-column label="操作" align="left" width="100"> | |
| 171 | + <template slot-scope="scope"> | |
| 172 | + <el-button v-if="scope.row.isEffective === 1" type="text" icon="el-icon-delete" | |
| 173 | + @click="deleteInventory(scope.row)" class="cancel-btn">作废</el-button> | |
| 174 | + </template> | |
| 175 | + </el-table-column> | |
| 176 | + </NCC-table> | |
| 177 | + <pagination :total="inventoryTotal" :page.sync="inventoryQuery.currentPage" | |
| 178 | + :limit.sync="inventoryQuery.pageSize" @pagination="initInventoryData" /> | |
| 179 | + </div> | |
| 180 | + </el-tab-pane> | |
| 181 | + | |
| 182 | + <!-- 使用记录Tab --> | |
| 183 | + <el-tab-pane label="使用记录" name="usage"> | |
| 184 | + <div class="tab-content"> | |
| 185 | + <div class="tab-header"> | |
| 186 | + <el-button type="primary" icon="el-icon-plus" size="small" @click="addUsage()">批量添加使用记录</el-button> | |
| 187 | + </div> | |
| 188 | + <NCC-table v-loading="usageLoading" :data="usageList" has-c | |
| 189 | + :header-cell-style="{ background: '#f5f7fa', color: '#606266' }"> | |
| 190 | + <el-table-column label="批次号" align="center"> | |
| 191 | + <template slot-scope="scope"> | |
| 192 | + <div class="usage-id-info"> | |
| 193 | + <i class="el-icon-postcard usage-id-icon"></i> | |
| 194 | + <span class="text-nowrap">{{ scope.row.usageBatchId || '无' }}</span> | |
| 195 | + </div> | |
| 196 | + </template> | |
| 197 | + </el-table-column> | |
| 198 | + <el-table-column label="门店名称" align="center"> | |
| 199 | + <template slot-scope="scope"> | |
| 200 | + <div class="store-name-info"> | |
| 201 | + <i class="el-icon-office-building store-name-icon"></i> | |
| 202 | + <span class="text-nowrap">{{ scope.row.storeName || '无' }}</span> | |
| 203 | + </div> | |
| 204 | + </template> | |
| 205 | + </el-table-column> | |
| 206 | + <el-table-column label="使用数量" align="center"> | |
| 207 | + <template slot-scope="scope"> | |
| 208 | + <div class="usage-quantity-info"> | |
| 209 | + <i class="el-icon-s-data usage-quantity-icon"></i> | |
| 210 | + <span class="text-nowrap">{{ scope.row.usageQuantity || 0 }}</span> | |
| 211 | + </div> | |
| 212 | + </template> | |
| 213 | + </el-table-column> | |
| 214 | + <el-table-column label="使用时间" align="center"> | |
| 215 | + <template slot-scope="scope"> | |
| 216 | + <div class="usage-time-info"> | |
| 217 | + <i class="el-icon-time usage-time-icon"></i> | |
| 218 | + <span class="text-nowrap">{{ formatDateTime(scope.row.usageTime) || '无' }}</span> | |
| 219 | + </div> | |
| 220 | + </template> | |
| 221 | + </el-table-column> | |
| 222 | + <!-- isEffective 是否有效 --> | |
| 223 | + <el-table-column label="是否有效" align="center"> | |
| 224 | + <template slot-scope="scope"> | |
| 225 | + <el-tag :type="scope.row.isEffective === 1 ? 'success' : 'info'" size="small"> | |
| 226 | + {{ scope.row.isEffective === 1 ? '有效' : '无效' }} | |
| 227 | + </el-tag> | |
| 228 | + </template> | |
| 229 | + </el-table-column> | |
| 230 | + <el-table-column label="操作" align="left" width="140"> | |
| 231 | + <template slot-scope="scope"> | |
| 232 | + <el-button v-if="scope.row.isEffective === 1" type="text" icon="el-icon-printer" | |
| 233 | + @click="handlePrint(scope.row)" class="edit-btn">打印</el-button> | |
| 234 | + <el-button v-if="scope.row.isEffective === 1" type="text" icon="el-icon-delete" | |
| 235 | + @click="deleteUsage(scope.row)" class="cancel-btn">作废</el-button> | |
| 236 | + </template> | |
| 237 | + </el-table-column> | |
| 238 | + </NCC-table> | |
| 239 | + <pagination :total="usageTotal" :page.sync="usageQuery.currentPage" | |
| 240 | + :limit.sync="usageQuery.pageSize" @pagination="initUsageData" /> | |
| 241 | + </div> | |
| 242 | + </el-tab-pane> | |
| 243 | + </el-tabs> | |
| 244 | + </div> | |
| 245 | + <span slot="footer" class="dialog-footer"> | |
| 246 | + <el-button @click="visible = false">关 闭</el-button> | |
| 247 | + </span> | |
| 248 | + | |
| 249 | + </el-dialog> | |
| 250 | + <!-- 库存表单弹窗 --> | |
| 251 | + <InventoryForm v-if="inventoryFormVisible" ref="InventoryForm" @refresh="refreshInventory" /> | |
| 252 | + <!-- 使用记录表单弹窗 --> | |
| 253 | + <UsageForm v-if="usageFormVisible" ref="UsageForm" @refresh="refreshUsage" /> | |
| 254 | + </div> | |
| 255 | +</template> | |
| 256 | + | |
| 257 | +<script> | |
| 258 | +import request from '@/utils/request' | |
| 259 | +import InventoryForm from './inventory-form.vue' | |
| 260 | +import UsageForm from './usage-form.vue' | |
| 261 | + | |
| 262 | +export default { | |
| 263 | + components: { InventoryForm, UsageForm }, | |
| 264 | + data() { | |
| 265 | + return { | |
| 266 | + visible: false, | |
| 267 | + activeTab: 'inventory', | |
| 268 | + productId: '', | |
| 269 | + productInfo: null, | |
| 270 | + // 库存相关 | |
| 271 | + inventoryList: [], | |
| 272 | + inventoryLoading: false, | |
| 273 | + inventoryTotal: 0, | |
| 274 | + inventoryQuery: { | |
| 275 | + currentPage: 1, | |
| 276 | + pageSize: 20, | |
| 277 | + ProductId: '' | |
| 278 | + }, | |
| 279 | + inventoryFormVisible: false, | |
| 280 | + // 使用记录相关 | |
| 281 | + usageList: [], | |
| 282 | + usageLoading: false, | |
| 283 | + usageTotal: 0, | |
| 284 | + usageQuery: { | |
| 285 | + currentPage: 1, | |
| 286 | + pageSize: 20, | |
| 287 | + ProductId: '', | |
| 288 | + StoreId: undefined | |
| 289 | + }, | |
| 290 | + usageFormVisible: false, | |
| 291 | + // 选项数据 | |
| 292 | + storeOptions: [] | |
| 293 | + } | |
| 294 | + }, | |
| 295 | + methods: { | |
| 296 | + init(productId, productInfo) { | |
| 297 | + this.visible = true | |
| 298 | + this.productId = productId | |
| 299 | + this.productInfo = productInfo | |
| 300 | + this.activeTab = 'inventory' | |
| 301 | + this.inventoryQuery.ProductId = productId | |
| 302 | + this.usageQuery.ProductId = productId | |
| 303 | + this.initInventoryData() | |
| 304 | + this.initStoreOptions() | |
| 305 | + }, | |
| 306 | + // 初始化门店选项 | |
| 307 | + initStoreOptions() { | |
| 308 | + request({ | |
| 309 | + url: '/api/Extend/LqMdxx', | |
| 310 | + method: 'GET', | |
| 311 | + data: { | |
| 312 | + currentPage: 1, | |
| 313 | + pageSize: 1000 | |
| 314 | + } | |
| 315 | + }).then(res => { | |
| 316 | + if (res.data && res.data.list) { | |
| 317 | + this.storeOptions = res.data.list | |
| 318 | + } | |
| 319 | + }).catch(() => { | |
| 320 | + this.storeOptions = [] | |
| 321 | + }) | |
| 322 | + }, | |
| 323 | + // 库存相关方法 | |
| 324 | + initInventoryData() { | |
| 325 | + this.inventoryLoading = true | |
| 326 | + let query = { ...this.inventoryQuery } | |
| 327 | + // 移除空值 | |
| 328 | + Object.keys(query).forEach(key => { | |
| 329 | + if (query[key] === undefined || query[key] === null || query[key] === '') { | |
| 330 | + delete query[key] | |
| 331 | + } | |
| 332 | + }) | |
| 333 | + request({ | |
| 334 | + url: '/api/Extend/LqInventory/GetList', | |
| 335 | + method: 'GET', | |
| 336 | + data: query | |
| 337 | + }).then(res => { | |
| 338 | + if (res.code == 200 && res.data) { | |
| 339 | + this.inventoryList = res.data.list || [] | |
| 340 | + this.inventoryTotal = res.data.pagination ? res.data.pagination.total : 0 | |
| 341 | + } else { | |
| 342 | + this.inventoryList = [] | |
| 343 | + this.inventoryTotal = 0 | |
| 344 | + } | |
| 345 | + this.inventoryLoading = false | |
| 346 | + }).catch(() => { | |
| 347 | + this.inventoryLoading = false | |
| 348 | + this.inventoryList = [] | |
| 349 | + this.inventoryTotal = 0 | |
| 350 | + }) | |
| 351 | + }, | |
| 352 | + addInventory() { | |
| 353 | + this.inventoryFormVisible = true | |
| 354 | + this.$nextTick(() => { | |
| 355 | + // 从详情页调用,传入产品ID,默认选中当前产品 | |
| 356 | + this.$refs.InventoryForm.init(this.productId) | |
| 357 | + }) | |
| 358 | + }, | |
| 359 | + refreshInventory() { | |
| 360 | + this.inventoryFormVisible = false | |
| 361 | + this.initInventoryData() | |
| 362 | + }, | |
| 363 | + // 作废库存 | |
| 364 | + deleteInventory(row) { | |
| 365 | + this.$confirm('确定要作废这条库存记录吗?', '提示', { | |
| 366 | + confirmButtonText: '确定', | |
| 367 | + cancelButtonText: '取消', | |
| 368 | + type: 'warning' | |
| 369 | + }).then(() => { | |
| 370 | + const remarks = encodeURIComponent('作废') | |
| 371 | + request({ | |
| 372 | + url: `/api/Extend/LqInventory/Cancel/${row.id}?remarks=${remarks}`, | |
| 373 | + method: 'PUT' | |
| 374 | + }).then(res => { | |
| 375 | + this.$message({ | |
| 376 | + type: 'success', | |
| 377 | + message: res.msg || '作废成功', | |
| 378 | + onClose: () => { | |
| 379 | + this.initInventoryData() | |
| 380 | + } | |
| 381 | + }) | |
| 382 | + }).catch(() => { | |
| 383 | + // 请求失败 | |
| 384 | + }) | |
| 385 | + }).catch(() => { | |
| 386 | + // 用户取消 | |
| 387 | + }) | |
| 388 | + }, | |
| 389 | + // 使用记录相关方法 | |
| 390 | + initUsageData() { | |
| 391 | + this.usageLoading = true | |
| 392 | + let query = { ...this.usageQuery } | |
| 393 | + // 移除空值 | |
| 394 | + Object.keys(query).forEach(key => { | |
| 395 | + if (query[key] === undefined || query[key] === null || query[key] === '') { | |
| 396 | + delete query[key] | |
| 397 | + } | |
| 398 | + }) | |
| 399 | + request({ | |
| 400 | + url: '/api/Extend/LqInventoryUsage/GetList', | |
| 401 | + method: 'GET', | |
| 402 | + data: query | |
| 403 | + }).then(res => { | |
| 404 | + if (res.code == 200 && res.data) { | |
| 405 | + this.usageList = res.data.list || [] | |
| 406 | + this.usageTotal = res.data.pagination ? res.data.pagination.total : 0 | |
| 407 | + } else { | |
| 408 | + this.usageList = [] | |
| 409 | + this.usageTotal = 0 | |
| 410 | + } | |
| 411 | + this.usageLoading = false | |
| 412 | + }).catch(() => { | |
| 413 | + this.usageLoading = false | |
| 414 | + this.usageList = [] | |
| 415 | + this.usageTotal = 0 | |
| 416 | + }) | |
| 417 | + }, | |
| 418 | + addUsage() { | |
| 419 | + this.usageFormVisible = true | |
| 420 | + this.$nextTick(() => { | |
| 421 | + this.$refs.UsageForm.init(this.productId) | |
| 422 | + }) | |
| 423 | + }, | |
| 424 | + refreshUsage() { | |
| 425 | + this.usageFormVisible = false | |
| 426 | + this.initUsageData() | |
| 427 | + }, | |
| 428 | + // 作废使用记录 | |
| 429 | + deleteUsage(row) { | |
| 430 | + this.$confirm('确定要作废这条使用记录吗?', '提示', { | |
| 431 | + confirmButtonText: '确定', | |
| 432 | + cancelButtonText: '取消', | |
| 433 | + type: 'warning' | |
| 434 | + }).then(() => { | |
| 435 | + request({ | |
| 436 | + url: `/api/Extend/LqInventoryUsage/Cancel?id=${row.id}`, | |
| 437 | + method: 'PUT' | |
| 438 | + }).then(res => { | |
| 439 | + this.$message({ | |
| 440 | + type: 'success', | |
| 441 | + message: res.msg || '作废成功', | |
| 442 | + onClose: () => { | |
| 443 | + this.initUsageData() | |
| 444 | + } | |
| 445 | + }) | |
| 446 | + }).catch(() => { | |
| 447 | + // 请求失败 | |
| 448 | + }) | |
| 449 | + }).catch(() => { | |
| 450 | + // 用户取消 | |
| 451 | + }) | |
| 452 | + }, | |
| 453 | + // 打印功能 | |
| 454 | + handlePrint(row) { | |
| 455 | + if (!row.usageBatchId) { | |
| 456 | + this.$message.warning('无法打印,批次ID不存在') | |
| 457 | + return | |
| 458 | + } | |
| 459 | + | |
| 460 | + // 调用接口获取批次信息 | |
| 461 | + request({ | |
| 462 | + url: `/api/Extend/LqInventoryUsage/GetBatchInfo?batchId=${row.usageBatchId}`, | |
| 463 | + method: 'GET' | |
| 464 | + }).then(res => { | |
| 465 | + if (res.code === 200 && res.data) { | |
| 466 | + // 构建打印内容 | |
| 467 | + let printContent = this.buildPrintContent(res.data) | |
| 468 | + | |
| 469 | + // 打开新窗口并打印 | |
| 470 | + let newWindow = window.open('_blank') | |
| 471 | + if (!newWindow) { | |
| 472 | + this.$message.error('无法打开打印窗口,请检查浏览器弹窗设置') | |
| 473 | + return | |
| 474 | + } | |
| 475 | + | |
| 476 | + newWindow.document.write(printContent) | |
| 477 | + newWindow.document.close() | |
| 478 | + | |
| 479 | + // 等待内容加载完成后打印 | |
| 480 | + setTimeout(() => { | |
| 481 | + newWindow.print() | |
| 482 | + }, 300) | |
| 483 | + } else { | |
| 484 | + this.$message.error(res.msg || '获取批次信息失败') | |
| 485 | + } | |
| 486 | + }).catch(err => { | |
| 487 | + this.$message.error('获取批次信息失败,请稍后重试') | |
| 488 | + }) | |
| 489 | + }, | |
| 490 | + // 构建打印内容 | |
| 491 | + buildPrintContent(batchData) { | |
| 492 | + // 格式化时间 | |
| 493 | + const formatDateTime = (timestamp) => { | |
| 494 | + if (!timestamp) return '无' | |
| 495 | + try { | |
| 496 | + const date = new Date(timestamp) | |
| 497 | + if (isNaN(date.getTime())) return '无' | |
| 498 | + const year = date.getFullYear() | |
| 499 | + const month = String(date.getMonth() + 1).padStart(2, '0') | |
| 500 | + const day = String(date.getDate()).padStart(2, '0') | |
| 501 | + const hours = String(date.getHours()).padStart(2, '0') | |
| 502 | + const minutes = String(date.getMinutes()).padStart(2, '0') | |
| 503 | + const seconds = String(date.getSeconds()).padStart(2, '0') | |
| 504 | + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}` | |
| 505 | + } catch (e) { | |
| 506 | + return '无' | |
| 507 | + } | |
| 508 | + } | |
| 509 | + | |
| 510 | + const formatDate = (timestamp) => { | |
| 511 | + if (!timestamp) return '无' | |
| 512 | + try { | |
| 513 | + const date = new Date(timestamp) | |
| 514 | + if (isNaN(date.getTime())) return '无' | |
| 515 | + const year = date.getFullYear() | |
| 516 | + const month = String(date.getMonth() + 1).padStart(2, '0') | |
| 517 | + const day = String(date.getDate()).padStart(2, '0') | |
| 518 | + return `${year}-${month}-${day}` | |
| 519 | + } catch (e) { | |
| 520 | + return '无' | |
| 521 | + } | |
| 522 | + } | |
| 523 | + | |
| 524 | + const formatMoney = (amount) => { | |
| 525 | + if (!amount && amount !== 0) return '0.00' | |
| 526 | + return Number(amount).toFixed(2) | |
| 527 | + } | |
| 528 | + | |
| 529 | + // 构建使用记录表格行 | |
| 530 | + let usageTableRows = '' | |
| 531 | + if (batchData.UsageRecords && batchData.UsageRecords.length > 0) { | |
| 532 | + batchData.UsageRecords.forEach((item, index) => { | |
| 533 | + usageTableRows += ` | |
| 534 | + <tr> | |
| 535 | + <td style="text-align: center;">${index + 1}</td> | |
| 536 | + <td>${item.productName || '无'}</td> | |
| 537 | + <td>${item.productCategory || '无'}</td> | |
| 538 | + <td style="text-align: right;">¥${formatMoney(item.productPrice)}</td> | |
| 539 | + <td>${item.storeName || '无'}</td> | |
| 540 | + <td style="text-align: center;">${item.usageQuantity || 0}</td> | |
| 541 | + <td style="text-align: right;">¥${formatMoney(item.usageTotalValue)}</td> | |
| 542 | + <td>${formatDateTime(item.usageTime)}</td> | |
| 543 | + <td>${item.createUserName || '无'}</td> | |
| 544 | + </tr> | |
| 545 | + ` | |
| 546 | + }) | |
| 547 | + } else { | |
| 548 | + usageTableRows = '<tr><td colspan="9" style="text-align: center;">暂无使用记录</td></tr>' | |
| 549 | + } | |
| 550 | + | |
| 551 | + return ` | |
| 552 | + <!DOCTYPE html> | |
| 553 | + <html> | |
| 554 | + <head> | |
| 555 | + <meta charset="UTF-8"> | |
| 556 | + <title>产品使用批次详情</title> | |
| 557 | + <style> | |
| 558 | + * { | |
| 559 | + margin: 0; | |
| 560 | + padding: 0; | |
| 561 | + box-sizing: border-box; | |
| 562 | + } | |
| 563 | + body { | |
| 564 | + font-family: "Microsoft YaHei", Arial, sans-serif; | |
| 565 | + font-size: 13px; | |
| 566 | + padding: 15px; | |
| 567 | + color: #333; | |
| 568 | + } | |
| 569 | + .print-header { | |
| 570 | + text-align: center; | |
| 571 | + margin-bottom: 15px; | |
| 572 | + border-bottom: 2px solid #333; | |
| 573 | + padding-bottom: 10px; | |
| 574 | + } | |
| 575 | + .print-header h1 { | |
| 576 | + font-size: 22px; | |
| 577 | + font-weight: bold; | |
| 578 | + margin-bottom: 5px; | |
| 579 | + } | |
| 580 | + .print-info { | |
| 581 | + margin-bottom: 12px; | |
| 582 | + } | |
| 583 | + .print-info table { | |
| 584 | + width: 100%; | |
| 585 | + border-collapse: collapse; | |
| 586 | + margin-bottom: 12px; | |
| 587 | + } | |
| 588 | + .print-info table td { | |
| 589 | + padding: 6px 10px; | |
| 590 | + border: 1px solid #ddd; | |
| 591 | + } | |
| 592 | + .print-info table td:first-child { | |
| 593 | + background-color: #f5f5f5; | |
| 594 | + font-weight: bold; | |
| 595 | + width: 140px; | |
| 596 | + text-align: right; | |
| 597 | + } | |
| 598 | + .print-table { | |
| 599 | + width: 100%; | |
| 600 | + border-collapse: collapse; | |
| 601 | + margin-bottom: 12px; | |
| 602 | + } | |
| 603 | + .print-table thead { | |
| 604 | + display: table-header-group; | |
| 605 | + } | |
| 606 | + .print-table tbody { | |
| 607 | + display: table-row-group; | |
| 608 | + } | |
| 609 | + .print-table th, | |
| 610 | + .print-table td { | |
| 611 | + border: 1px solid #ddd; | |
| 612 | + padding: 5px 6px; | |
| 613 | + text-align: left; | |
| 614 | + font-size: 12px; | |
| 615 | + } | |
| 616 | + .print-table th { | |
| 617 | + background-color: #f5f5f5; | |
| 618 | + font-weight: bold; | |
| 619 | + text-align: center; | |
| 620 | + } | |
| 621 | + .print-table tbody tr { | |
| 622 | + page-break-inside: avoid; | |
| 623 | + } | |
| 624 | + .print-footer { | |
| 625 | + margin-top: 15px; | |
| 626 | + padding-top: 10px; | |
| 627 | + border-top: 1px solid #ddd; | |
| 628 | + text-align: right; | |
| 629 | + font-size: 11px; | |
| 630 | + color: #666; | |
| 631 | + } | |
| 632 | + @media print { | |
| 633 | + body { | |
| 634 | + padding: 8px; | |
| 635 | + } | |
| 636 | + .print-header { | |
| 637 | + page-break-after: avoid; | |
| 638 | + margin-bottom: 12px; | |
| 639 | + padding-bottom: 8px; | |
| 640 | + } | |
| 641 | + .print-info { | |
| 642 | + page-break-after: avoid; | |
| 643 | + margin-bottom: 10px; | |
| 644 | + } | |
| 645 | + .print-table { | |
| 646 | + page-break-inside: auto; | |
| 647 | + } | |
| 648 | + .print-table thead { | |
| 649 | + display: table-header-group; | |
| 650 | + } | |
| 651 | + .print-table tbody tr { | |
| 652 | + page-break-inside: avoid; | |
| 653 | + } | |
| 654 | + } | |
| 655 | + </style> | |
| 656 | + </head> | |
| 657 | + <body> | |
| 658 | + <div class="print-header"> | |
| 659 | + <h1>产品使用批次详情</h1> | |
| 660 | + </div> | |
| 661 | + | |
| 662 | + <div class="print-info"> | |
| 663 | + <table> | |
| 664 | + <tr> | |
| 665 | + <td>批次ID:</td> | |
| 666 | + <td>${batchData.BatchId || '无'}</td> | |
| 667 | + </tr> | |
| 668 | + <tr> | |
| 669 | + <td>创建时间:</td> | |
| 670 | + <td>${formatDateTime(batchData.CreateTime)}</td> | |
| 671 | + </tr> | |
| 672 | + <tr> | |
| 673 | + <td>创建人:</td> | |
| 674 | + <td>${batchData.CreateUserName || batchData.CreateUser || '无'}</td> | |
| 675 | + </tr> | |
| 676 | + <tr> | |
| 677 | + <td>总记录数:</td> | |
| 678 | + <td>${batchData.TotalCount || 0}</td> | |
| 679 | + </tr> | |
| 680 | + <tr> | |
| 681 | + <td>有效记录数:</td> | |
| 682 | + <td>${batchData.EffectiveCount || 0}</td> | |
| 683 | + </tr> | |
| 684 | + <tr> | |
| 685 | + <td>无效记录数:</td> | |
| 686 | + <td>${batchData.IneffectiveCount || 0}</td> | |
| 687 | + </tr> | |
| 688 | + <tr> | |
| 689 | + <td>总使用数量:</td> | |
| 690 | + <td>${batchData.TotalUsageQuantity || 0}</td> | |
| 691 | + </tr> | |
| 692 | + <tr> | |
| 693 | + <td>总使用金额:</td> | |
| 694 | + <td>¥${formatMoney(batchData.TotalUsageAmount)}</td> | |
| 695 | + </tr> | |
| 696 | + </table> | |
| 697 | + </div> | |
| 698 | + | |
| 699 | + <div class="print-info"> | |
| 700 | + <h3 style="margin-bottom: 8px; font-size: 14px;">使用记录明细</h3> | |
| 701 | + <table class="print-table"> | |
| 702 | + <thead> | |
| 703 | + <tr> | |
| 704 | + <th style="width: 50px;">序号</th> | |
| 705 | + <th>产品名称</th> | |
| 706 | + <th>产品类别</th> | |
| 707 | + <th style="width: 100px;">单价</th> | |
| 708 | + <th>门店名称</th> | |
| 709 | + <th style="width: 80px;">使用数量</th> | |
| 710 | + <th style="width: 100px;">使用金额</th> | |
| 711 | + <th style="width: 150px;">使用时间</th> | |
| 712 | + <th>创建人</th> | |
| 713 | + </tr> | |
| 714 | + </thead> | |
| 715 | + <tbody> | |
| 716 | + ${usageTableRows} | |
| 717 | + </tbody> | |
| 718 | + </table> | |
| 719 | + </div> | |
| 720 | + | |
| 721 | + <div class="print-footer"> | |
| 722 | + <p>打印时间:${formatDateTime(new Date().getTime())}</p> | |
| 723 | + </div> | |
| 724 | + </body> | |
| 725 | + </html> | |
| 726 | + ` | |
| 727 | + }, | |
| 728 | + // 工具方法 | |
| 729 | + formatMoney(amount) { | |
| 730 | + if (!amount && amount !== 0) return '0.00' | |
| 731 | + return Number(amount).toFixed(2) | |
| 732 | + }, | |
| 733 | + formatDate(date) { | |
| 734 | + if (!date) return '无' | |
| 735 | + try { | |
| 736 | + const d = new Date(date) | |
| 737 | + if (isNaN(d.getTime())) return '无' | |
| 738 | + const year = d.getFullYear() | |
| 739 | + const month = String(d.getMonth() + 1).padStart(2, '0') | |
| 740 | + const day = String(d.getDate()).padStart(2, '0') | |
| 741 | + return `${year}-${month}-${day}` | |
| 742 | + } catch (e) { | |
| 743 | + return date | |
| 744 | + } | |
| 745 | + }, | |
| 746 | + formatDateTime(dateTime) { | |
| 747 | + if (!dateTime) return '无' | |
| 748 | + try { | |
| 749 | + const date = new Date(dateTime) | |
| 750 | + if (isNaN(date.getTime())) return '无' | |
| 751 | + const year = date.getFullYear() | |
| 752 | + const month = String(date.getMonth() + 1).padStart(2, '0') | |
| 753 | + const day = String(date.getDate()).padStart(2, '0') | |
| 754 | + const hours = String(date.getHours()).padStart(2, '0') | |
| 755 | + const minutes = String(date.getMinutes()).padStart(2, '0') | |
| 756 | + const seconds = String(date.getSeconds()).padStart(2, '0') | |
| 757 | + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}` | |
| 758 | + } catch (e) { | |
| 759 | + return dateTime | |
| 760 | + } | |
| 761 | + } | |
| 762 | + }, | |
| 763 | + watch: { | |
| 764 | + activeTab(newVal) { | |
| 765 | + if (newVal === 'usage' && this.usageList.length === 0) { | |
| 766 | + this.initUsageData() | |
| 767 | + } | |
| 768 | + } | |
| 769 | + } | |
| 770 | +} | |
| 771 | +</script> | |
| 772 | + | |
| 773 | +<style lang="scss" scoped> | |
| 774 | +.detail-content { | |
| 775 | + .info-card { | |
| 776 | + margin-bottom: 20px; | |
| 777 | + | |
| 778 | + .card-header { | |
| 779 | + display: flex; | |
| 780 | + align-items: center; | |
| 781 | + gap: 8px; | |
| 782 | + | |
| 783 | + i { | |
| 784 | + color: #409EFF; | |
| 785 | + font-size: 18px; | |
| 786 | + } | |
| 787 | + | |
| 788 | + .card-title { | |
| 789 | + font-weight: 600; | |
| 790 | + font-size: 16px; | |
| 791 | + } | |
| 792 | + } | |
| 793 | + | |
| 794 | + .info-item { | |
| 795 | + margin-bottom: 15px; | |
| 796 | + display: flex; | |
| 797 | + align-items: center; | |
| 798 | + | |
| 799 | + .info-label { | |
| 800 | + color: #909399; | |
| 801 | + font-weight: 500; | |
| 802 | + min-width: 100px; | |
| 803 | + text-align: right; | |
| 804 | + margin-right: 8px; | |
| 805 | + flex-shrink: 0; | |
| 806 | + } | |
| 807 | + | |
| 808 | + .info-value { | |
| 809 | + color: #303133; | |
| 810 | + flex: 1; | |
| 811 | + } | |
| 812 | + } | |
| 813 | + } | |
| 814 | + | |
| 815 | + .detail-tabs { | |
| 816 | + margin-top: 20px; | |
| 817 | + | |
| 818 | + .tab-content { | |
| 819 | + .tab-header { | |
| 820 | + margin-bottom: 15px; | |
| 821 | + display: flex; | |
| 822 | + justify-content: flex-start; | |
| 823 | + } | |
| 824 | + } | |
| 825 | + } | |
| 826 | +} | |
| 827 | + | |
| 828 | +// 通用文本不换行样式 | |
| 829 | +.text-nowrap { | |
| 830 | + white-space: nowrap; | |
| 831 | + overflow: hidden; | |
| 832 | + text-overflow: ellipsis; | |
| 833 | + max-width: 100%; | |
| 834 | +} | |
| 835 | + | |
| 836 | +// 库存相关样式 | |
| 837 | +.inventory-id-info, | |
| 838 | +.quantity-info, | |
| 839 | +.stock-time-info, | |
| 840 | +.production-date-info, | |
| 841 | +.shelf-life-info, | |
| 842 | +.batch-number-info { | |
| 843 | + display: flex; | |
| 844 | + align-items: center; | |
| 845 | + justify-content: center; | |
| 846 | + gap: 6px; | |
| 847 | +} | |
| 848 | + | |
| 849 | +.inventory-id-icon { | |
| 850 | + color: #409EFF; | |
| 851 | + font-size: 16px; | |
| 852 | +} | |
| 853 | + | |
| 854 | +.quantity-icon { | |
| 855 | + color: #67C23A; | |
| 856 | + font-size: 16px; | |
| 857 | +} | |
| 858 | + | |
| 859 | +.stock-time-icon, | |
| 860 | +.production-date-icon { | |
| 861 | + color: #909399; | |
| 862 | + font-size: 16px; | |
| 863 | +} | |
| 864 | + | |
| 865 | +.shelf-life-icon, | |
| 866 | +.batch-number-icon { | |
| 867 | + color: #E6A23C; | |
| 868 | + font-size: 16px; | |
| 869 | +} | |
| 870 | + | |
| 871 | +// 使用记录相关样式 | |
| 872 | +.usage-id-info, | |
| 873 | +.store-name-info, | |
| 874 | +.usage-quantity-info, | |
| 875 | +.usage-time-info, | |
| 876 | +.related-consume-info { | |
| 877 | + display: flex; | |
| 878 | + align-items: center; | |
| 879 | + justify-content: center; | |
| 880 | + gap: 6px; | |
| 881 | +} | |
| 882 | + | |
| 883 | +.usage-id-icon { | |
| 884 | + color: #409EFF; | |
| 885 | + font-size: 16px; | |
| 886 | +} | |
| 887 | + | |
| 888 | +.store-name-icon { | |
| 889 | + color: #67C23A; | |
| 890 | + font-size: 16px; | |
| 891 | +} | |
| 892 | + | |
| 893 | +.usage-quantity-icon { | |
| 894 | + color: #409EFF; | |
| 895 | + font-size: 16px; | |
| 896 | +} | |
| 897 | + | |
| 898 | +.usage-time-icon { | |
| 899 | + color: #909399; | |
| 900 | + font-size: 16px; | |
| 901 | +} | |
| 902 | + | |
| 903 | +.related-consume-icon { | |
| 904 | + color: #E6A23C; | |
| 905 | + font-size: 16px; | |
| 906 | +} | |
| 907 | + | |
| 908 | +// 作废按钮样式 | |
| 909 | +.cancel-btn { | |
| 910 | + color: #F56C6C !important; | |
| 911 | + &:hover { | |
| 912 | + color: #f78989 !important; | |
| 913 | + } | |
| 914 | +} | |
| 915 | +</style> | |
| 916 | + | ... | ... |
antis-ncc-admin/src/views/lqInventory/product-form.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog :title="!dataForm.id ? '新建产品' : '编辑产品'" :close-on-click-modal="false" :visible.sync="visible" | |
| 3 | + class="NCC-dialog NCC-dialog_center" lock-scroll width="800px"> | |
| 4 | + <el-form ref="elForm" :model="dataForm" size="small" label-width="120px" label-position="right" :rules="rules"> | |
| 5 | + <el-row :gutter="15"> | |
| 6 | + <el-col :span="12"> | |
| 7 | + <el-form-item label="产品名称" prop="productName"> | |
| 8 | + <el-input v-model="dataForm.productName" placeholder="请输入产品名称" clearable | |
| 9 | + :style='{"width":"100%"}' /> | |
| 10 | + </el-form-item> | |
| 11 | + </el-col> | |
| 12 | + <el-col :span="12"> | |
| 13 | + <el-form-item label="价格" prop="price"> | |
| 14 | + <el-input-number v-model="dataForm.price" placeholder="请输入价格" :precision="2" :min="0" | |
| 15 | + :style='{"width":"100%"}' /> | |
| 16 | + </el-form-item> | |
| 17 | + </el-col> | |
| 18 | + <el-col :span="12"> | |
| 19 | + <el-form-item label="产品类别" prop="productCategory"> | |
| 20 | + <el-input v-model="dataForm.productCategory" placeholder="请输入产品类别" clearable | |
| 21 | + :style='{"width":"100%"}' /> | |
| 22 | + </el-form-item> | |
| 23 | + </el-col> | |
| 24 | + <el-col :span="12"> | |
| 25 | + <el-form-item label="负责部门" prop="departmentId"> | |
| 26 | + <el-select v-model="dataForm.departmentId" placeholder="请选择负责部门" clearable filterable | |
| 27 | + :style='{"width":"100%"}'> | |
| 28 | + <el-option v-for="dept in departmentList" :key="dept.id" :label="dept.name" | |
| 29 | + :value="dept.id" /> | |
| 30 | + </el-select> | |
| 31 | + </el-form-item> | |
| 32 | + </el-col> | |
| 33 | + <el-col :span="12"> | |
| 34 | + <el-form-item label="标准单位" prop="standardUnit"> | |
| 35 | + <el-input v-model="dataForm.standardUnit" placeholder="请输入标准单位" clearable | |
| 36 | + :style='{"width":"100%"}' /> | |
| 37 | + </el-form-item> | |
| 38 | + </el-col> | |
| 39 | + <!-- <el-col :span="12"> | |
| 40 | + <el-form-item label="上架状态" prop="onShelfStatus"> | |
| 41 | + <el-select v-model="dataForm.onShelfStatus" placeholder="请选择上架状态" clearable | |
| 42 | + :style='{"width":"100%"}'> | |
| 43 | + <el-option label="上架" :value="1" /> | |
| 44 | + <el-option label="下架" :value="0" /> | |
| 45 | + </el-select> | |
| 46 | + </el-form-item> | |
| 47 | + </el-col> --> | |
| 48 | + <el-col :span="12"> | |
| 49 | + <el-form-item label="统计分类" prop="statisticsCategory"> | |
| 50 | + <el-input v-model="dataForm.statisticsCategory" placeholder="请输入统计分类" clearable | |
| 51 | + :style='{"width":"100%"}' /> | |
| 52 | + </el-form-item> | |
| 53 | + </el-col> | |
| 54 | + <el-col :span="12"> | |
| 55 | + <el-form-item label="归属仓库" prop="warehouse"> | |
| 56 | + <el-input v-model="dataForm.warehouse" placeholder="请输入归属仓库" clearable | |
| 57 | + :style='{"width":"100%"}' /> | |
| 58 | + </el-form-item> | |
| 59 | + </el-col> | |
| 60 | + <el-col :span="12"> | |
| 61 | + <el-form-item label="供应商名称" prop="supplierName"> | |
| 62 | + <el-input v-model="dataForm.supplierName" placeholder="请输入供应商名称" clearable | |
| 63 | + :style='{"width":"100%"}' /> | |
| 64 | + </el-form-item> | |
| 65 | + </el-col> | |
| 66 | + <el-col :span="12"> | |
| 67 | + <el-form-item label="合同签订日期" prop="contractSignDate"> | |
| 68 | + <el-date-picker v-model="dataForm.contractSignDate" type="date" placeholder="请选择合同签订日期" | |
| 69 | + value-format="yyyy-MM-dd" format="yyyy-MM-dd" :style='{"width":"100%"}' /> | |
| 70 | + </el-form-item> | |
| 71 | + </el-col> | |
| 72 | + <el-col :span="12"> | |
| 73 | + <el-form-item label="合同结束日期" prop="contractEndDate"> | |
| 74 | + <el-date-picker v-model="dataForm.contractEndDate" type="date" placeholder="请选择合同结束日期" | |
| 75 | + value-format="yyyy-MM-dd" format="yyyy-MM-dd" :style='{"width":"100%"}' /> | |
| 76 | + </el-form-item> | |
| 77 | + </el-col> | |
| 78 | + <el-col :span="24"> | |
| 79 | + <el-form-item label="备注" prop="remark"> | |
| 80 | + <el-input v-model="dataForm.remark" type="textarea" :rows="3" placeholder="请输入备注信息" | |
| 81 | + clearable :style='{"width":"100%"}' /> | |
| 82 | + </el-form-item> | |
| 83 | + </el-col> | |
| 84 | + </el-row> | |
| 85 | + </el-form> | |
| 86 | + <span slot="footer" class="dialog-footer"> | |
| 87 | + <el-button @click="visible = false">取 消</el-button> | |
| 88 | + <el-button type="primary" @click="dataFormSubmit()" :loading="btnLoading">确 定</el-button> | |
| 89 | + </span> | |
| 90 | + </el-dialog> | |
| 91 | +</template> | |
| 92 | + | |
| 93 | +<script> | |
| 94 | +import request from '@/utils/request' | |
| 95 | + | |
| 96 | +export default { | |
| 97 | + data() { | |
| 98 | + return { | |
| 99 | + visible: false, | |
| 100 | + btnLoading: false, | |
| 101 | + departmentList: [], // 部门列表 | |
| 102 | + dataForm: { | |
| 103 | + id: undefined, | |
| 104 | + productName: '', | |
| 105 | + price: 0, | |
| 106 | + productCategory: '', | |
| 107 | + departmentId: '', | |
| 108 | + standardUnit: '', | |
| 109 | + onShelfStatus: 1, | |
| 110 | + statisticsCategory: '', | |
| 111 | + warehouse: '', | |
| 112 | + supplierName: '', | |
| 113 | + contractSignDate: '', | |
| 114 | + contractEndDate: '', | |
| 115 | + remark: '' | |
| 116 | + }, | |
| 117 | + rules: { | |
| 118 | + productName: [ | |
| 119 | + { required: true, message: '请输入产品名称', trigger: 'blur' } | |
| 120 | + ], | |
| 121 | + price: [ | |
| 122 | + { required: true, message: '请输入价格', trigger: 'blur' } | |
| 123 | + ], | |
| 124 | + onShelfStatus: [ | |
| 125 | + { required: true, message: '请选择上架状态', trigger: 'change' } | |
| 126 | + ] | |
| 127 | + } | |
| 128 | + } | |
| 129 | + }, | |
| 130 | + methods: { | |
| 131 | + init(id) { | |
| 132 | + this.visible = true | |
| 133 | + this.dataForm = { | |
| 134 | + id: undefined, | |
| 135 | + productName: '', | |
| 136 | + price: 0, | |
| 137 | + productCategory: '', | |
| 138 | + departmentId: '', | |
| 139 | + standardUnit: '', | |
| 140 | + onShelfStatus: 1, | |
| 141 | + statisticsCategory: '', | |
| 142 | + warehouse: '', | |
| 143 | + supplierName: '', | |
| 144 | + contractSignDate: '', | |
| 145 | + contractEndDate: '', | |
| 146 | + remark: '' | |
| 147 | + } | |
| 148 | + this.getDepartmentList() | |
| 149 | + if (id) { | |
| 150 | + // 编辑模式,获取产品详情 | |
| 151 | + this.getProductDetail(id) | |
| 152 | + } | |
| 153 | + }, | |
| 154 | + // 获取部门列表 | |
| 155 | + getDepartmentList() { | |
| 156 | + request({ | |
| 157 | + url: `/api/permission/Organize/96240625-934F-490B-8AA6-0BC775B18468/Department`, | |
| 158 | + method: 'GET', | |
| 159 | + }).then((res) => { | |
| 160 | + if (res.code == 200 && res.data.list && res.data.list.length > 0) { | |
| 161 | + this.departmentList = res.data.list.map(item => ({ | |
| 162 | + id: item.id, | |
| 163 | + name: item.fullName, | |
| 164 | + })) | |
| 165 | + } else { | |
| 166 | + this.departmentList = [] | |
| 167 | + } | |
| 168 | + }).catch(() => { | |
| 169 | + this.departmentList = [] | |
| 170 | + }) | |
| 171 | + }, | |
| 172 | + getProductDetail(id) { | |
| 173 | + request({ | |
| 174 | + url: `/api/Extend/LqProduct/GetList`, | |
| 175 | + method: 'GET', | |
| 176 | + data: { | |
| 177 | + currentPage: 1, | |
| 178 | + pageSize: 1, | |
| 179 | + id: id | |
| 180 | + } | |
| 181 | + }).then(res => { | |
| 182 | + if (res.code == 200 && res.data && res.data.list && res.data.list.length > 0) { | |
| 183 | + const product = res.data.list[0] | |
| 184 | + // 处理日期格式:从ISO格式转换为 yyyy-MM-dd 格式 | |
| 185 | + let contractSignDate = product.contractSignDate || '' | |
| 186 | + let contractEndDate = product.contractEndDate || '' | |
| 187 | + if (contractSignDate && contractSignDate.includes('T')) { | |
| 188 | + const date = new Date(contractSignDate) | |
| 189 | + const year = date.getFullYear() | |
| 190 | + const month = String(date.getMonth() + 1).padStart(2, '0') | |
| 191 | + const day = String(date.getDate()).padStart(2, '0') | |
| 192 | + contractSignDate = `${year}-${month}-${day}` | |
| 193 | + } | |
| 194 | + if (contractEndDate && contractEndDate.includes('T')) { | |
| 195 | + const date = new Date(contractEndDate) | |
| 196 | + const year = date.getFullYear() | |
| 197 | + const month = String(date.getMonth() + 1).padStart(2, '0') | |
| 198 | + const day = String(date.getDate()).padStart(2, '0') | |
| 199 | + contractEndDate = `${year}-${month}-${day}` | |
| 200 | + } | |
| 201 | + this.dataForm = { | |
| 202 | + id: product.id, | |
| 203 | + productName: product.productName || '', | |
| 204 | + price: product.price || 0, | |
| 205 | + productCategory: product.productCategory || '', | |
| 206 | + departmentId: product.departmentId || '', | |
| 207 | + standardUnit: product.standardUnit || '', | |
| 208 | + onShelfStatus: product.onShelfStatus !== undefined ? product.onShelfStatus : 1, | |
| 209 | + statisticsCategory: product.statisticsCategory || '', | |
| 210 | + warehouse: product.warehouse || '', | |
| 211 | + supplierName: product.supplierName || '', | |
| 212 | + contractSignDate: contractSignDate, | |
| 213 | + contractEndDate: contractEndDate, | |
| 214 | + remark: product.remark || '' | |
| 215 | + } | |
| 216 | + } | |
| 217 | + }).catch(() => { | |
| 218 | + this.$message({ | |
| 219 | + type: 'error', | |
| 220 | + message: '获取产品详情失败' | |
| 221 | + }) | |
| 222 | + }) | |
| 223 | + }, | |
| 224 | + dataFormSubmit() { | |
| 225 | + this.$refs.elForm.validate((valid) => { | |
| 226 | + if (!valid) { | |
| 227 | + return false | |
| 228 | + } | |
| 229 | + this.btnLoading = true | |
| 230 | + const url = this.dataForm.id ? '/api/Extend/LqProduct/Update' : '/api/Extend/LqProduct/Create' | |
| 231 | + const method = this.dataForm.id ? 'PUT' : 'POST' | |
| 232 | + // 处理日期格式,确保提交ISO格式 | |
| 233 | + let contractSignDate = this.dataForm.contractSignDate | |
| 234 | + let contractEndDate = this.dataForm.contractEndDate | |
| 235 | + if (contractSignDate && !contractSignDate.includes('T')) { | |
| 236 | + const date = new Date(contractSignDate) | |
| 237 | + contractSignDate = date.toISOString() | |
| 238 | + } | |
| 239 | + if (contractEndDate && !contractEndDate.includes('T')) { | |
| 240 | + const date = new Date(contractEndDate) | |
| 241 | + contractEndDate = date.toISOString() | |
| 242 | + } | |
| 243 | + request({ | |
| 244 | + url: url, | |
| 245 | + method: method, | |
| 246 | + data: { | |
| 247 | + id: this.dataForm.id, | |
| 248 | + productName: this.dataForm.productName, | |
| 249 | + price: this.dataForm.price, | |
| 250 | + productCategory: this.dataForm.productCategory, | |
| 251 | + departmentId: this.dataForm.departmentId, | |
| 252 | + standardUnit: this.dataForm.standardUnit, | |
| 253 | + onShelfStatus: this.dataForm.onShelfStatus, | |
| 254 | + statisticsCategory: this.dataForm.statisticsCategory, | |
| 255 | + warehouse: this.dataForm.warehouse, | |
| 256 | + supplierName: this.dataForm.supplierName, | |
| 257 | + contractSignDate: contractSignDate, | |
| 258 | + contractEndDate: contractEndDate, | |
| 259 | + remark: this.dataForm.remark | |
| 260 | + } | |
| 261 | + }).then(res => { | |
| 262 | + this.btnLoading = false | |
| 263 | + this.$message({ | |
| 264 | + type: 'success', | |
| 265 | + message: res.msg || (this.dataForm.id ? '编辑成功' : '创建成功'), | |
| 266 | + onClose: () => { | |
| 267 | + this.visible = false | |
| 268 | + this.$emit('refresh') | |
| 269 | + } | |
| 270 | + }) | |
| 271 | + }).catch(() => { | |
| 272 | + this.btnLoading = false | |
| 273 | + }) | |
| 274 | + }) | |
| 275 | + } | |
| 276 | + } | |
| 277 | +} | |
| 278 | +</script> | |
| 279 | + | |
| 280 | +<style lang="scss" scoped> | |
| 281 | +</style> | |
| 282 | + | ... | ... |
antis-ncc-admin/src/views/lqInventory/usage-form.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog title="批量添加使用记录" :close-on-click-modal="false" :visible.sync="visible" | |
| 3 | + class="NCC-dialog NCC-dialog_center" lock-scroll width="900px"> | |
| 4 | + <el-form ref="elForm" :model="dataForm" size="small" label-width="80px" label-position="right" :rules="rules"> | |
| 5 | + <div class="usage-items-container"> | |
| 6 | + <div v-for="(item, index) in dataForm.usageItems" :key="index" class="usage-item-row"> | |
| 7 | + <el-row :gutter="15"> | |
| 8 | + <el-col :span="12"> | |
| 9 | + <el-form-item label="产品" :prop="`usageItems.${index}.productId`" | |
| 10 | + :rules="rules.productId"> | |
| 11 | + <el-select v-model="item.productId" placeholder="请选择产品" clearable filterable | |
| 12 | + :style='{"width":"100%"}' :disabled="!!defaultProductId"> | |
| 13 | + <el-option v-for="product in productOptions" :key="product.id" | |
| 14 | + :label="product.productName" :value="product.id" /> | |
| 15 | + </el-select> | |
| 16 | + </el-form-item> | |
| 17 | + </el-col> | |
| 18 | + <el-col :span="12"> | |
| 19 | + <el-form-item label="门店" :prop="`usageItems.${index}.storeId`" | |
| 20 | + :rules="rules.storeId"> | |
| 21 | + <el-select v-model="item.storeId" placeholder="请选择门店" clearable filterable | |
| 22 | + :style='{"width":"100%"}'> | |
| 23 | + <el-option v-for="store in storeOptions" :key="store.id" :label="store.dm" | |
| 24 | + :value="store.id" /> | |
| 25 | + </el-select> | |
| 26 | + </el-form-item> | |
| 27 | + </el-col> | |
| 28 | + <el-col :span="12"> | |
| 29 | + <el-form-item label="使用数量" | |
| 30 | + :prop="`usageItems.${index}.usageQuantity`" :rules="rules.usageQuantity"> | |
| 31 | + <el-input-number v-model="item.usageQuantity" placeholder="数量" :min="1" :precision="0" | |
| 32 | + :style='{"width":"100%"}' /> | |
| 33 | + </el-form-item> | |
| 34 | + </el-col> | |
| 35 | + <el-col :span="12"> | |
| 36 | + <el-form-item label="使用时间" | |
| 37 | + :prop="`usageItems.${index}.usageTime`" :rules="rules.usageTime"> | |
| 38 | + <el-date-picker v-model="item.usageTime" type="datetime" placeholder="使用时间" | |
| 39 | + value-format="yyyy-MM-dd HH:mm:ss" format="yyyy-MM-dd HH:mm:ss" | |
| 40 | + :style='{"width":"100%"}' /> | |
| 41 | + </el-form-item> | |
| 42 | + </el-col> | |
| 43 | + <!-- <el-col :span="24"> | |
| 44 | + <el-form-item :label="index === 0 ? '关联消费ID' : ''" | |
| 45 | + :prop="`usageItems.${index}.relatedConsumeId`"> | |
| 46 | + <el-input v-model="item.relatedConsumeId" placeholder="请输入关联消费ID(可选)" clearable | |
| 47 | + :style='{"width":"100%"}' /> | |
| 48 | + </el-form-item> | |
| 49 | + </el-col> --> | |
| 50 | + <el-col :span="24" v-if="dataForm.usageItems.length > 1"> | |
| 51 | + <div class="item-actions"> | |
| 52 | + <el-button type="text" icon="el-icon-delete" @click="removeItem(index)" | |
| 53 | + class="delete-btn">删除</el-button> | |
| 54 | + </div> | |
| 55 | + </el-col> | |
| 56 | + </el-row> | |
| 57 | + <el-divider v-if="index < dataForm.usageItems.length - 1"></el-divider> | |
| 58 | + </div> | |
| 59 | + </div> | |
| 60 | + <div class="add-item-btn"> | |
| 61 | + <el-button type="text" icon="el-icon-plus" @click="addItem()">添加一条记录</el-button> | |
| 62 | + </div> | |
| 63 | + </el-form> | |
| 64 | + <span slot="footer" class="dialog-footer"> | |
| 65 | + <el-button @click="visible = false">取 消</el-button> | |
| 66 | + <el-button type="primary" @click="dataFormSubmit()" :loading="btnLoading">确 定</el-button> | |
| 67 | + </span> | |
| 68 | + </el-dialog> | |
| 69 | +</template> | |
| 70 | + | |
| 71 | +<script> | |
| 72 | +import request from '@/utils/request' | |
| 73 | + | |
| 74 | +export default { | |
| 75 | + data() { | |
| 76 | + return { | |
| 77 | + visible: false, | |
| 78 | + btnLoading: false, | |
| 79 | + defaultProductId: '', | |
| 80 | + dataForm: { | |
| 81 | + usageItems: [ | |
| 82 | + { | |
| 83 | + productId: '', | |
| 84 | + storeId: '', | |
| 85 | + usageTime: '', | |
| 86 | + usageQuantity: 1, | |
| 87 | + relatedConsumeId: '' | |
| 88 | + } | |
| 89 | + ] | |
| 90 | + }, | |
| 91 | + productOptions: [], | |
| 92 | + storeOptions: [], | |
| 93 | + rules: { | |
| 94 | + productId: [ | |
| 95 | + { required: true, message: '请选择产品', trigger: 'change' } | |
| 96 | + ], | |
| 97 | + storeId: [ | |
| 98 | + { required: true, message: '请选择门店', trigger: 'change' } | |
| 99 | + ], | |
| 100 | + usageQuantity: [ | |
| 101 | + { required: true, message: '请输入使用数量', trigger: 'blur' } | |
| 102 | + ], | |
| 103 | + usageTime: [ | |
| 104 | + { required: true, message: '请选择使用时间', trigger: 'change' } | |
| 105 | + ] | |
| 106 | + } | |
| 107 | + } | |
| 108 | + }, | |
| 109 | + methods: { | |
| 110 | + init(productId) { | |
| 111 | + this.visible = true | |
| 112 | + this.defaultProductId = productId || '' | |
| 113 | + this.dataForm = { | |
| 114 | + usageItems: [ | |
| 115 | + { | |
| 116 | + productId: productId || '', | |
| 117 | + storeId: '', | |
| 118 | + usageTime: '', | |
| 119 | + usageQuantity: 1, | |
| 120 | + relatedConsumeId: '' | |
| 121 | + } | |
| 122 | + ] | |
| 123 | + } | |
| 124 | + this.initProductOptions() | |
| 125 | + this.initStoreOptions() | |
| 126 | + }, | |
| 127 | + initProductOptions() { | |
| 128 | + request({ | |
| 129 | + url: '/api/Extend/LqProduct/GetList', | |
| 130 | + method: 'GET', | |
| 131 | + data: { | |
| 132 | + currentPage: 1, | |
| 133 | + pageSize: 1000 | |
| 134 | + } | |
| 135 | + }).then(res => { | |
| 136 | + if (res.code == 200 && res.data && res.data.list) { | |
| 137 | + this.productOptions = res.data.list | |
| 138 | + } | |
| 139 | + }).catch(() => { | |
| 140 | + this.productOptions = [] | |
| 141 | + }) | |
| 142 | + }, | |
| 143 | + initStoreOptions() { | |
| 144 | + request({ | |
| 145 | + url: '/api/Extend/LqMdxx', | |
| 146 | + method: 'GET', | |
| 147 | + data: { | |
| 148 | + currentPage: 1, | |
| 149 | + pageSize: 1000 | |
| 150 | + } | |
| 151 | + }).then(res => { | |
| 152 | + if (res.data && res.data.list) { | |
| 153 | + this.storeOptions = res.data.list | |
| 154 | + } | |
| 155 | + }).catch(() => { | |
| 156 | + this.storeOptions = [] | |
| 157 | + }) | |
| 158 | + }, | |
| 159 | + addItem() { | |
| 160 | + this.dataForm.usageItems.push({ | |
| 161 | + productId: this.defaultProductId || '', | |
| 162 | + storeId: '', | |
| 163 | + usageTime: '', | |
| 164 | + usageQuantity: 1, | |
| 165 | + relatedConsumeId: '' | |
| 166 | + }) | |
| 167 | + }, | |
| 168 | + removeItem(index) { | |
| 169 | + if (this.dataForm.usageItems.length > 1) { | |
| 170 | + this.dataForm.usageItems.splice(index, 1) | |
| 171 | + } else { | |
| 172 | + this.$message({ | |
| 173 | + type: 'warning', | |
| 174 | + message: '至少保留一条记录' | |
| 175 | + }) | |
| 176 | + } | |
| 177 | + }, | |
| 178 | + dataFormSubmit() { | |
| 179 | + // 验证所有表单项 | |
| 180 | + let isValid = true | |
| 181 | + this.dataForm.usageItems.forEach((item, index) => { | |
| 182 | + if (!item.productId) { | |
| 183 | + this.$message({ | |
| 184 | + type: 'error', | |
| 185 | + message: `第${index + 1}条记录:请选择产品` | |
| 186 | + }) | |
| 187 | + isValid = false | |
| 188 | + } | |
| 189 | + if (!item.storeId) { | |
| 190 | + this.$message({ | |
| 191 | + type: 'error', | |
| 192 | + message: `第${index + 1}条记录:请选择门店` | |
| 193 | + }) | |
| 194 | + isValid = false | |
| 195 | + } | |
| 196 | + if (!item.usageQuantity || item.usageQuantity <= 0) { | |
| 197 | + this.$message({ | |
| 198 | + type: 'error', | |
| 199 | + message: `第${index + 1}条记录:请输入使用数量` | |
| 200 | + }) | |
| 201 | + isValid = false | |
| 202 | + } | |
| 203 | + if (!item.usageTime) { | |
| 204 | + this.$message({ | |
| 205 | + type: 'error', | |
| 206 | + message: `第${index + 1}条记录:请选择使用时间` | |
| 207 | + }) | |
| 208 | + isValid = false | |
| 209 | + } | |
| 210 | + }) | |
| 211 | + | |
| 212 | + if (!isValid) { | |
| 213 | + return false | |
| 214 | + } | |
| 215 | + | |
| 216 | + this.btnLoading = true | |
| 217 | + // 转换日期时间为ISO格式 | |
| 218 | + const usageItems = this.dataForm.usageItems.map(item => { | |
| 219 | + let usageTime = item.usageTime | |
| 220 | + // 如果日期时间不是ISO格式,转换为ISO格式 | |
| 221 | + if (usageTime && !usageTime.includes('T')) { | |
| 222 | + const date = new Date(usageTime) | |
| 223 | + usageTime = date.toISOString() | |
| 224 | + } | |
| 225 | + return { | |
| 226 | + productId: item.productId, | |
| 227 | + storeId: item.storeId, | |
| 228 | + usageTime: usageTime, | |
| 229 | + usageQuantity: item.usageQuantity, | |
| 230 | + relatedConsumeId: item.relatedConsumeId || '' | |
| 231 | + } | |
| 232 | + }) | |
| 233 | + request({ | |
| 234 | + url: '/api/Extend/LqInventoryUsage/BatchCreate', | |
| 235 | + method: 'POST', | |
| 236 | + data: { | |
| 237 | + usageItems: usageItems | |
| 238 | + } | |
| 239 | + }).then(res => { | |
| 240 | + this.btnLoading = false | |
| 241 | + this.$message({ | |
| 242 | + type: 'success', | |
| 243 | + message: res.msg || '添加成功', | |
| 244 | + onClose: () => { | |
| 245 | + this.visible = false | |
| 246 | + this.$emit('refresh') | |
| 247 | + } | |
| 248 | + }) | |
| 249 | + }).catch(() => { | |
| 250 | + this.btnLoading = false | |
| 251 | + }) | |
| 252 | + } | |
| 253 | + } | |
| 254 | +} | |
| 255 | +</script> | |
| 256 | + | |
| 257 | +<style lang="scss" scoped> | |
| 258 | +.usage-items-container { | |
| 259 | + // max-height: 500px; | |
| 260 | + // overflow-y: scroll; | |
| 261 | + padding: 10px 0; | |
| 262 | +} | |
| 263 | + | |
| 264 | +.usage-item-row { | |
| 265 | + margin-bottom: 10px; | |
| 266 | +} | |
| 267 | + | |
| 268 | +.item-actions { | |
| 269 | + display: flex; | |
| 270 | + justify-content: flex-end; | |
| 271 | + margin-top: 10px; | |
| 272 | + | |
| 273 | + .delete-btn { | |
| 274 | + color: #F56C6C; | |
| 275 | + | |
| 276 | + &:hover { | |
| 277 | + color: #f78989; | |
| 278 | + } | |
| 279 | + } | |
| 280 | +} | |
| 281 | + | |
| 282 | +.add-item-btn { | |
| 283 | + margin-top: 10px; | |
| 284 | + text-align: center; | |
| 285 | + padding: 10px 0; | |
| 286 | + border-top: 1px dashed #dcdfe6; | |
| 287 | +} | |
| 288 | +</style> | |
| 289 | + | ... | ... |
antis-ncc-admin/src/views/lqInventory/usage-multi-form.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog title="添加使用记录" :close-on-click-modal="false" :visible.sync="visible" | |
| 3 | + class="NCC-dialog NCC-dialog_center" lock-scroll width="900px"> | |
| 4 | + <el-form ref="elForm" :model="dataForm" size="small" label-width="80px" label-position="right" :rules="rules"> | |
| 5 | + <div class="usage-items-container"> | |
| 6 | + <div v-for="(item, index) in dataForm.usageItems" :key="index" class="usage-item-row"> | |
| 7 | + <el-row :gutter="15"> | |
| 8 | + <el-col :span="12"> | |
| 9 | + <el-form-item label="产品" :prop="`usageItems.${index}.productId`" | |
| 10 | + :rules="rules.productId"> | |
| 11 | + <el-select v-model="item.productId" placeholder="请选择产品" clearable filterable | |
| 12 | + :style='{"width":"100%"}' @change="onProductChange(item, index)"> | |
| 13 | + <el-option v-for="product in productOptions" :key="product.id" | |
| 14 | + :label="product.productName" :value="product.id" /> | |
| 15 | + </el-select> | |
| 16 | + </el-form-item> | |
| 17 | + </el-col> | |
| 18 | + <el-col :span="12"> | |
| 19 | + <el-form-item label="门店" :prop="`usageItems.${index}.storeId`" | |
| 20 | + :rules="rules.storeId"> | |
| 21 | + <el-select v-model="item.storeId" placeholder="请选择门店" clearable filterable | |
| 22 | + :style='{"width":"100%"}'> | |
| 23 | + <el-option v-for="store in storeOptions" :key="store.id" :label="store.dm" | |
| 24 | + :value="store.id" /> | |
| 25 | + </el-select> | |
| 26 | + </el-form-item> | |
| 27 | + </el-col> | |
| 28 | + <el-col :span="12"> | |
| 29 | + <el-form-item label="使用数量" | |
| 30 | + :prop="`usageItems.${index}.usageQuantity`" :rules="rules.usageQuantity"> | |
| 31 | + <el-input-number v-model="item.usageQuantity" placeholder="数量" :min="1" :precision="0" | |
| 32 | + :style='{"width":"100%"}' /> | |
| 33 | + </el-form-item> | |
| 34 | + </el-col> | |
| 35 | + <el-col :span="12"> | |
| 36 | + <el-form-item label="使用时间" | |
| 37 | + :prop="`usageItems.${index}.usageTime`" :rules="rules.usageTime"> | |
| 38 | + <el-date-picker v-model="item.usageTime" type="datetime" placeholder="使用时间" | |
| 39 | + value-format="yyyy-MM-dd HH:mm:ss" format="yyyy-MM-dd HH:mm:ss" | |
| 40 | + :style='{"width":"100%"}' /> | |
| 41 | + </el-form-item> | |
| 42 | + </el-col> | |
| 43 | + <el-col :span="24" v-if="dataForm.usageItems.length > 1"> | |
| 44 | + <div class="item-actions"> | |
| 45 | + <el-button type="text" icon="el-icon-delete" @click="removeItem(index)" | |
| 46 | + class="delete-btn">删除</el-button> | |
| 47 | + </div> | |
| 48 | + </el-col> | |
| 49 | + </el-row> | |
| 50 | + <el-divider v-if="index < dataForm.usageItems.length - 1"></el-divider> | |
| 51 | + </div> | |
| 52 | + </div> | |
| 53 | + <div class="add-item-btn"> | |
| 54 | + <el-button type="text" icon="el-icon-plus" @click="addItem()">添加一条记录</el-button> | |
| 55 | + </div> | |
| 56 | + </el-form> | |
| 57 | + <span slot="footer" class="dialog-footer"> | |
| 58 | + <el-button @click="visible = false">取 消</el-button> | |
| 59 | + <el-button type="primary" @click="dataFormSubmit()" :loading="btnLoading">确 定</el-button> | |
| 60 | + </span> | |
| 61 | + </el-dialog> | |
| 62 | +</template> | |
| 63 | + | |
| 64 | +<script> | |
| 65 | +import request from '@/utils/request' | |
| 66 | + | |
| 67 | +export default { | |
| 68 | + data() { | |
| 69 | + return { | |
| 70 | + visible: false, | |
| 71 | + btnLoading: false, | |
| 72 | + dataForm: { | |
| 73 | + usageItems: [ | |
| 74 | + { | |
| 75 | + productId: '', | |
| 76 | + storeId: '', | |
| 77 | + usageTime: '', | |
| 78 | + usageQuantity: 1, | |
| 79 | + relatedConsumeId: '' | |
| 80 | + } | |
| 81 | + ] | |
| 82 | + }, | |
| 83 | + productOptions: [], | |
| 84 | + storeOptions: [], | |
| 85 | + rules: { | |
| 86 | + productId: [ | |
| 87 | + { required: true, message: '请选择产品', trigger: 'change' } | |
| 88 | + ], | |
| 89 | + storeId: [ | |
| 90 | + { required: true, message: '请选择门店', trigger: 'change' } | |
| 91 | + ], | |
| 92 | + usageQuantity: [ | |
| 93 | + { required: true, message: '请输入使用数量', trigger: 'blur' } | |
| 94 | + ], | |
| 95 | + usageTime: [ | |
| 96 | + { required: true, message: '请选择使用时间', trigger: 'change' } | |
| 97 | + ] | |
| 98 | + } | |
| 99 | + } | |
| 100 | + }, | |
| 101 | + methods: { | |
| 102 | + init() { | |
| 103 | + this.visible = true | |
| 104 | + this.dataForm = { | |
| 105 | + usageItems: [ | |
| 106 | + { | |
| 107 | + productId: '', | |
| 108 | + storeId: '', | |
| 109 | + usageTime: '', | |
| 110 | + usageQuantity: 1, | |
| 111 | + relatedConsumeId: '' | |
| 112 | + } | |
| 113 | + ] | |
| 114 | + } | |
| 115 | + this.initProductOptions() | |
| 116 | + this.initStoreOptions() | |
| 117 | + }, | |
| 118 | + initProductOptions() { | |
| 119 | + request({ | |
| 120 | + url: '/api/Extend/LqProduct/GetList', | |
| 121 | + method: 'GET', | |
| 122 | + data: { | |
| 123 | + currentPage: 1, | |
| 124 | + pageSize: 1000, | |
| 125 | + onShelfStatus: 1 // 只获取上架的产品 | |
| 126 | + } | |
| 127 | + }).then(res => { | |
| 128 | + if (res.code == 200 && res.data && res.data.list) { | |
| 129 | + // 过滤出上架的产品 | |
| 130 | + this.productOptions = res.data.list.filter(product => product.onShelfStatus === 1) | |
| 131 | + } | |
| 132 | + }).catch(() => { | |
| 133 | + this.productOptions = [] | |
| 134 | + }) | |
| 135 | + }, | |
| 136 | + initStoreOptions() { | |
| 137 | + request({ | |
| 138 | + url: '/api/Extend/LqMdxx', | |
| 139 | + method: 'GET', | |
| 140 | + data: { | |
| 141 | + currentPage: 1, | |
| 142 | + pageSize: 1000 | |
| 143 | + } | |
| 144 | + }).then(res => { | |
| 145 | + if (res.data && res.data.list) { | |
| 146 | + this.storeOptions = res.data.list | |
| 147 | + } | |
| 148 | + }).catch(() => { | |
| 149 | + this.storeOptions = [] | |
| 150 | + }) | |
| 151 | + }, | |
| 152 | + onProductChange(item, index) { | |
| 153 | + // 产品选择变化时的处理 | |
| 154 | + }, | |
| 155 | + addItem() { | |
| 156 | + this.dataForm.usageItems.push({ | |
| 157 | + productId: '', | |
| 158 | + storeId: '', | |
| 159 | + usageTime: '', | |
| 160 | + usageQuantity: 1, | |
| 161 | + relatedConsumeId: '' | |
| 162 | + }) | |
| 163 | + }, | |
| 164 | + removeItem(index) { | |
| 165 | + if (this.dataForm.usageItems.length > 1) { | |
| 166 | + this.dataForm.usageItems.splice(index, 1) | |
| 167 | + } else { | |
| 168 | + this.$message({ | |
| 169 | + type: 'warning', | |
| 170 | + message: '至少保留一条记录' | |
| 171 | + }) | |
| 172 | + } | |
| 173 | + }, | |
| 174 | + dataFormSubmit() { | |
| 175 | + // 验证所有表单项 | |
| 176 | + let isValid = true | |
| 177 | + this.dataForm.usageItems.forEach((item, index) => { | |
| 178 | + if (!item.productId) { | |
| 179 | + this.$message({ | |
| 180 | + type: 'error', | |
| 181 | + message: `第${index + 1}条记录:请选择产品` | |
| 182 | + }) | |
| 183 | + isValid = false | |
| 184 | + } | |
| 185 | + if (!item.storeId) { | |
| 186 | + this.$message({ | |
| 187 | + type: 'error', | |
| 188 | + message: `第${index + 1}条记录:请选择门店` | |
| 189 | + }) | |
| 190 | + isValid = false | |
| 191 | + } | |
| 192 | + if (!item.usageQuantity || item.usageQuantity <= 0) { | |
| 193 | + this.$message({ | |
| 194 | + type: 'error', | |
| 195 | + message: `第${index + 1}条记录:请输入使用数量` | |
| 196 | + }) | |
| 197 | + isValid = false | |
| 198 | + } | |
| 199 | + if (!item.usageTime) { | |
| 200 | + this.$message({ | |
| 201 | + type: 'error', | |
| 202 | + message: `第${index + 1}条记录:请选择使用时间` | |
| 203 | + }) | |
| 204 | + isValid = false | |
| 205 | + } | |
| 206 | + }) | |
| 207 | + | |
| 208 | + if (!isValid) { | |
| 209 | + return false | |
| 210 | + } | |
| 211 | + | |
| 212 | + this.btnLoading = true | |
| 213 | + // 转换日期时间为ISO格式 | |
| 214 | + const usageItems = this.dataForm.usageItems.map(item => { | |
| 215 | + let usageTime = item.usageTime | |
| 216 | + // 如果日期时间不是ISO格式,转换为ISO格式 | |
| 217 | + if (usageTime && !usageTime.includes('T')) { | |
| 218 | + const date = new Date(usageTime) | |
| 219 | + usageTime = date.toISOString() | |
| 220 | + } | |
| 221 | + return { | |
| 222 | + productId: item.productId, | |
| 223 | + storeId: item.storeId, | |
| 224 | + usageTime: usageTime, | |
| 225 | + usageQuantity: item.usageQuantity, | |
| 226 | + relatedConsumeId: item.relatedConsumeId || '' | |
| 227 | + } | |
| 228 | + }) | |
| 229 | + request({ | |
| 230 | + url: '/api/Extend/LqInventoryUsage/BatchCreate', | |
| 231 | + method: 'POST', | |
| 232 | + data: { | |
| 233 | + usageItems: usageItems | |
| 234 | + } | |
| 235 | + }).then(res => { | |
| 236 | + this.btnLoading = false | |
| 237 | + this.$message({ | |
| 238 | + type: 'success', | |
| 239 | + message: res.msg || '添加成功', | |
| 240 | + onClose: () => { | |
| 241 | + this.visible = false | |
| 242 | + this.$emit('refresh') | |
| 243 | + } | |
| 244 | + }) | |
| 245 | + }).catch(() => { | |
| 246 | + this.btnLoading = false | |
| 247 | + }) | |
| 248 | + } | |
| 249 | + } | |
| 250 | +} | |
| 251 | +</script> | |
| 252 | + | |
| 253 | +<style lang="scss" scoped> | |
| 254 | +.usage-items-container { | |
| 255 | + padding: 10px 0; | |
| 256 | +} | |
| 257 | + | |
| 258 | +.usage-item-row { | |
| 259 | + margin-bottom: 10px; | |
| 260 | +} | |
| 261 | + | |
| 262 | +.item-actions { | |
| 263 | + display: flex; | |
| 264 | + justify-content: flex-end; | |
| 265 | + margin-top: 10px; | |
| 266 | + | |
| 267 | + .delete-btn { | |
| 268 | + color: #F56C6C; | |
| 269 | + | |
| 270 | + &:hover { | |
| 271 | + color: #f78989; | |
| 272 | + } | |
| 273 | + } | |
| 274 | +} | |
| 275 | + | |
| 276 | +.add-item-btn { | |
| 277 | + margin-top: 10px; | |
| 278 | + text-align: center; | |
| 279 | + padding: 10px 0; | |
| 280 | + border-top: 1px dashed #dcdfe6; | |
| 281 | +} | |
| 282 | +</style> | |
| 283 | + | ... | ... |
antis-ncc-admin/src/views/statisticsList/form1.vue
| ... | ... | @@ -19,9 +19,7 @@ |
| 19 | 19 | value-format="timestamp" |
| 20 | 20 | format="yyyy-MM-dd HH:mm:ss" |
| 21 | 21 | placeholder="开始时间" |
| 22 | - > | |
| 23 | - </el-date-picker> | |
| 24 | - <!-- :picker-options="startTimePickerOptions" --> | |
| 22 | + /> | |
| 25 | 23 | </el-form-item> |
| 26 | 24 | </el-col> |
| 27 | 25 | <el-col :span="6"> |
| ... | ... | @@ -32,15 +30,14 @@ |
| 32 | 30 | value-format="timestamp" |
| 33 | 31 | format="yyyy-MM-dd HH:mm:ss" |
| 34 | 32 | placeholder="结束时间" |
| 35 | - > | |
| 36 | - <!-- :picker-options="endTimePickerOptions" --> | |
| 37 | - </el-date-picker> | |
| 33 | + /> | |
| 38 | 34 | </el-form-item> |
| 39 | 35 | </el-col> |
| 40 | 36 | <el-col :span="6"> |
| 41 | 37 | <el-form-item> |
| 42 | 38 | <el-button type="primary" icon="el-icon-search" @click="search()">查询</el-button> |
| 43 | 39 | <el-button icon="el-icon-refresh-right" @click="reset()">重置</el-button> |
| 40 | + <el-button icon="el-icon-setting" @click="showColumnDialog = true">字段设置</el-button> | |
| 44 | 41 | </el-form-item> |
| 45 | 42 | </el-col> |
| 46 | 43 | </el-form> |
| ... | ... | @@ -143,109 +140,128 @@ |
| 143 | 140 | </div> |
| 144 | 141 | |
| 145 | 142 | <!-- 数据表格 --> |
| 146 | - <div class="NCC-common-layout-main NCC-flex-main"> | |
| 147 | - <!-- <div class="NCC-common-head"> | |
| 148 | - <div> | |
| 149 | - <el-button type="text" icon="el-icon-download" @click="exportData()">导出</el-button> | |
| 150 | - </div> | |
| 151 | - <div class="NCC-common-head-right"> | |
| 152 | - <el-tooltip effect="dark" content="刷新" placement="top"> | |
| 153 | - <el-link icon="icon-ym icon-ym-Refresh NCC-common-head-icon" :underline="false" @click="reset()" /> | |
| 154 | - </el-tooltip> | |
| 155 | - <screenfull isContainer /> | |
| 156 | - </div> | |
| 157 | - </div> --> | |
| 158 | - | |
| 159 | - <NCC-table v-loading="listLoading" :data="records" has-c> | |
| 160 | - <el-table-column prop="date" label="开单日期" align="left" width="120"> | |
| 143 | + <div class="NCC-common-layout-main NCC-flex-main table-wrapper"> | |
| 144 | + <el-table | |
| 145 | + v-loading="listLoading" | |
| 146 | + :data="records" | |
| 147 | + border | |
| 148 | + class="NCC-common-table" | |
| 149 | + :height="tableHeight" | |
| 150 | + @sort-change="handleSortChange"> | |
| 151 | + <el-table-column | |
| 152 | + v-for="col in visibleColumns" | |
| 153 | + :key="col.prop" | |
| 154 | + :prop="col.prop" | |
| 155 | + :label="col.label" | |
| 156 | + :width="col.width" | |
| 157 | + :min-width="col.minWidth" | |
| 158 | + :sortable="col.sortable ? 'custom' : false" | |
| 159 | + :align="col.align || 'left'"> | |
| 161 | 160 | <template slot-scope="scope"> |
| 162 | - {{ formatDate(scope.row.date) }} | |
| 163 | - </template> | |
| 164 | - </el-table-column> | |
| 165 | - <el-table-column prop="customerName" label="客户姓名" align="left" width="100" /> | |
| 166 | - <el-table-column prop="customerPhone" label="客户电话" align="left" width="120" /> | |
| 167 | - <el-table-column prop="goldTriangle" label="金三角" align="left" width="100" /> | |
| 168 | - <el-table-column prop="customerType" label="客户类型" align="left" width="100"> | |
| 169 | - <template slot-scope="scope"> | |
| 170 | - <el-tag :type="scope.row.customerType === '会员' ? 'success' : 'info'" size="small"> | |
| 161 | + <!-- 开单日期 --> | |
| 162 | + <span v-if="col.prop === 'date'"> | |
| 163 | + {{ formatDate(scope.row.date) }} | |
| 164 | + </span> | |
| 165 | + <!-- 客户类型 --> | |
| 166 | + <el-tag v-else-if="col.prop === 'customerType'" :type="scope.row.customerType === '会员' ? 'success' : 'info'" size="small"> | |
| 171 | 167 | {{ scope.row.customerType }} |
| 172 | 168 | </el-tag> |
| 173 | - </template> | |
| 174 | - </el-table-column> | |
| 175 | - <el-table-column label="购买项目" align="left" min-width="200"> | |
| 176 | - <template slot-scope="scope"> | |
| 177 | - <div v-if="scope.row.purchasedItems && scope.row.purchasedItems.length > 0"> | |
| 178 | - <div v-for="item in scope.row.purchasedItems" :key="item.id" class="item-row"> | |
| 179 | - <span class="item-name">{{ item.itemName }}</span> | |
| 180 | - <span class="item-info">({{ item.projectNumber }}次 × ¥{{ item.price }})</span> | |
| 169 | + <!-- 购买项目 --> | |
| 170 | + <div v-else-if="col.prop === 'purchasedItems'"> | |
| 171 | + <div v-if="scope.row.purchasedItems && scope.row.purchasedItems.length > 0"> | |
| 172 | + <div v-for="(item, itemIndex) in scope.row.purchasedItems" :key="`purchased-${scope.$index}-${itemIndex}-${item.id || itemIndex}`" class="item-row"> | |
| 173 | + <span class="item-name">{{ item.itemName }}</span> | |
| 174 | + <span class="item-info">({{ item.projectNumber }}次 × ¥{{ item.price }})</span> | |
| 175 | + </div> | |
| 181 | 176 | </div> |
| 177 | + <span v-else class="no-data">无</span> | |
| 182 | 178 | </div> |
| 183 | - <span v-else class="no-data">无</span> | |
| 184 | - </template> | |
| 185 | - </el-table-column> | |
| 186 | - <el-table-column label="赠送项目" align="left" min-width="200"> | |
| 187 | - <template slot-scope="scope"> | |
| 188 | - <div v-if="scope.row.giftedItems && scope.row.giftedItems.length > 0"> | |
| 189 | - <div v-for="item in scope.row.giftedItems" :key="item.id" class="item-row"> | |
| 190 | - <span class="item-name">{{ item.itemName }}</span> | |
| 191 | - <span class="item-info">({{ item.projectNumber }}次)</span> | |
| 192 | - <span v-if="item.remark" class="item-remark">{{ item.remark }}</span> | |
| 179 | + <!-- 赠送项目 --> | |
| 180 | + <div v-else-if="col.prop === 'giftedItems'"> | |
| 181 | + <div v-if="scope.row.giftedItems && scope.row.giftedItems.length > 0"> | |
| 182 | + <div v-for="(item, itemIndex) in scope.row.giftedItems" :key="`gifted-${scope.$index}-${itemIndex}-${item.id || itemIndex}`" class="item-row"> | |
| 183 | + <span class="item-name">{{ item.itemName }}</span> | |
| 184 | + <span class="item-info">({{ item.projectNumber }}次)</span> | |
| 185 | + <span v-if="item.remark" class="item-remark">{{ item.remark }}</span> | |
| 186 | + </div> | |
| 193 | 187 | </div> |
| 188 | + <span v-else class="no-data">无</span> | |
| 194 | 189 | </div> |
| 195 | - <span v-else class="no-data">无</span> | |
| 196 | - </template> | |
| 197 | - </el-table-column> | |
| 198 | - <el-table-column label="体验项目" align="left" min-width="200"> | |
| 199 | - <template slot-scope="scope"> | |
| 200 | - <div v-if="scope.row.experienceItems && scope.row.experienceItems.length > 0"> | |
| 201 | - <div v-for="item in scope.row.experienceItems" :key="item.id" class="item-row"> | |
| 202 | - <span class="item-name">{{ item.itemName }}</span> | |
| 203 | - <span class="item-info">({{ item.projectNumber }}次)</span> | |
| 190 | + <!-- 体验项目 --> | |
| 191 | + <div v-else-if="col.prop === 'experienceItems'"> | |
| 192 | + <div v-if="scope.row.experienceItems && scope.row.experienceItems.length > 0"> | |
| 193 | + <div v-for="(item, itemIndex) in scope.row.experienceItems" :key="`experience-${scope.$index}-${itemIndex}-${item.id || itemIndex}`" class="item-row"> | |
| 194 | + <span class="item-name">{{ item.itemName }}</span> | |
| 195 | + <span class="item-info">({{ item.projectNumber }}次)</span> | |
| 196 | + </div> | |
| 204 | 197 | </div> |
| 198 | + <span v-else class="no-data">无</span> | |
| 205 | 199 | </div> |
| 206 | - <span v-else class="no-data">无</span> | |
| 207 | - </template> | |
| 208 | - </el-table-column> | |
| 209 | - <el-table-column label="健康师" align="left" min-width="150"> | |
| 210 | - <template slot-scope="scope"> | |
| 211 | - <div v-if="scope.row.healthTeachers && scope.row.healthTeachers.length > 0"> | |
| 212 | - <div v-for="teacher in scope.row.healthTeachers" :key="teacher.teacherId" class="teacher-row"> | |
| 213 | - <span class="teacher-name">{{ teacher.teacherName }}</span> | |
| 214 | - <span class="teacher-performance">业绩: ¥{{ teacher.performance }}</span> | |
| 200 | + <!-- 健康师 --> | |
| 201 | + <div v-else-if="col.prop === 'healthTeachers'"> | |
| 202 | + <div v-if="scope.row.healthTeachers && scope.row.healthTeachers.length > 0"> | |
| 203 | + <div v-for="(teacher, teacherIndex) in scope.row.healthTeachers" :key="`teacher-${scope.$index}-${teacherIndex}-${teacher.teacherId || teacher.teacherName || teacherIndex}`" class="teacher-row"> | |
| 204 | + <span class="teacher-name">{{ teacher.teacherName }}</span> | |
| 205 | + <span class="teacher-performance">业绩: ¥{{ teacher.performance }}</span> | |
| 206 | + </div> | |
| 215 | 207 | </div> |
| 208 | + <span v-else class="no-data">无</span> | |
| 216 | 209 | </div> |
| 217 | - <span v-else class="no-data">无</span> | |
| 218 | - </template> | |
| 219 | - </el-table-column> | |
| 220 | - <el-table-column prop="paidAmount" label="已付金额" align="left" width="100"> | |
| 221 | - <template slot-scope="scope"> | |
| 222 | - <span class="amount-paid">¥{{ formatMoney(scope.row.paidAmount) }}</span> | |
| 223 | - </template> | |
| 224 | - </el-table-column> | |
| 225 | - <el-table-column prop="debtAmount" label="欠款金额" align="left" width="100"> | |
| 226 | - <template slot-scope="scope"> | |
| 227 | - <span class="amount-debt">¥{{ formatMoney(scope.row.debtAmount) }}</span> | |
| 228 | - </template> | |
| 229 | - </el-table-column> | |
| 230 | - <el-table-column prop="totalAmount" label="总金额" align="left" width="100"> | |
| 231 | - <template slot-scope="scope"> | |
| 232 | - <span class="amount-total">¥{{ formatMoney(scope.row.totalAmount) }}</span> | |
| 233 | - </template> | |
| 234 | - </el-table-column> | |
| 235 | - <el-table-column prop="paymentMethod" label="支付方式" align="left" width="100" /> | |
| 236 | - <el-table-column prop="remark" label="备注" align="left" min-width="150"> | |
| 237 | - <template slot-scope="scope"> | |
| 238 | - {{ scope.row.remark || '无' }} | |
| 239 | - </template> | |
| 240 | - </el-table-column> | |
| 241 | - <el-table-column label="开单时间" prop="createTime" align="left" width="150"> | |
| 242 | - <template slot-scope="scope"> | |
| 243 | - {{ formatTime(scope.row.createTime) }} | |
| 210 | + <!-- 已付金额 --> | |
| 211 | + <span v-else-if="col.prop === 'paidAmount'" class="amount-paid"> | |
| 212 | + ¥{{ formatMoney(scope.row.paidAmount) }} | |
| 213 | + </span> | |
| 214 | + <!-- 欠款金额 --> | |
| 215 | + <span v-else-if="col.prop === 'debtAmount'" class="amount-debt"> | |
| 216 | + ¥{{ formatMoney(scope.row.debtAmount) }} | |
| 217 | + </span> | |
| 218 | + <!-- 总金额 --> | |
| 219 | + <span v-else-if="col.prop === 'totalAmount'" class="amount-total"> | |
| 220 | + ¥{{ formatMoney(scope.row.totalAmount) }} | |
| 221 | + </span> | |
| 222 | + <!-- 备注 --> | |
| 223 | + <span v-else-if="col.prop === 'remark'"> | |
| 224 | + {{ scope.row.remark || '无' }} | |
| 225 | + </span> | |
| 226 | + <!-- 开单时间 --> | |
| 227 | + <span v-else-if="col.prop === 'createTime'"> | |
| 228 | + {{ formatTime(scope.row.createTime) }} | |
| 229 | + </span> | |
| 230 | + <!-- 默认显示 --> | |
| 231 | + <span v-else> | |
| 232 | + {{ scope.row[col.prop] !== null && scope.row[col.prop] !== undefined ? scope.row[col.prop] : '无' }} | |
| 233 | + </span> | |
| 244 | 234 | </template> |
| 245 | 235 | </el-table-column> |
| 246 | - </NCC-table> | |
| 236 | + <template slot="empty"> | |
| 237 | + <el-empty description="暂无数据" :image-size="120"></el-empty> | |
| 238 | + </template> | |
| 239 | + </el-table> | |
| 247 | 240 | </div> |
| 248 | 241 | </div> |
| 242 | + | |
| 243 | + <!-- 字段设置弹窗 --> | |
| 244 | + <el-dialog title="字段设置" :visible.sync="showColumnDialog" width="500px" append-to-body> | |
| 245 | + <div class="column-settings"> | |
| 246 | + <div class="column-actions" style="margin-bottom: 15px;"> | |
| 247 | + <el-button size="small" @click="selectAllColumns">全选</el-button> | |
| 248 | + <el-button size="small" @click="clearAllColumns">清空</el-button> | |
| 249 | + </div> | |
| 250 | + <el-checkbox-group v-model="selectedColumns"> | |
| 251 | + <el-checkbox | |
| 252 | + v-for="col in columnOptions" | |
| 253 | + :key="col.prop" | |
| 254 | + :label="col.prop" | |
| 255 | + style="display: block; margin-bottom: 10px;"> | |
| 256 | + {{ col.label }} | |
| 257 | + </el-checkbox> | |
| 258 | + </el-checkbox-group> | |
| 259 | + </div> | |
| 260 | + <div slot="footer" class="dialog-footer"> | |
| 261 | + <el-button @click="showColumnDialog = false">取消</el-button> | |
| 262 | + <el-button type="primary" @click="saveColumnSettings">确定</el-button> | |
| 263 | + </div> | |
| 264 | + </el-dialog> | |
| 249 | 265 | </div> |
| 250 | 266 | </template> |
| 251 | 267 | |
| ... | ... | @@ -265,41 +281,47 @@ export default { |
| 265 | 281 | summaryData: null, |
| 266 | 282 | records: [], |
| 267 | 283 | listLoading: false, |
| 268 | - startTimePickerOptions: { | |
| 269 | - disabledDate: (time) => { | |
| 270 | - const timeMs = time.getTime() | |
| 271 | - const nowMs = Date.now() | |
| 272 | - const ninetyDaysMs = 90 * 24 * 60 * 60 * 1000 | |
| 273 | - // 不能选择未来日期 | |
| 274 | - if (timeMs > nowMs) return true | |
| 275 | - // 若已选择结束时间:开始时间需在 [end-90天, end] 区间内 | |
| 276 | - if (this.query.endTime) { | |
| 277 | - const minStart = this.query.endTime - ninetyDaysMs | |
| 278 | - return timeMs < minStart || timeMs > this.query.endTime | |
| 279 | - } | |
| 280 | - return false | |
| 281 | - } | |
| 284 | + showColumnDialog: false, | |
| 285 | + selectedColumns: [], | |
| 286 | + sortInfo: { | |
| 287 | + prop: '', | |
| 288 | + order: '' | |
| 282 | 289 | }, |
| 283 | - endTimePickerOptions: { | |
| 284 | - disabledDate: (time) => { | |
| 285 | - const timeMs = time.getTime() | |
| 286 | - const nowMs = Date.now() | |
| 287 | - const ninetyDaysMs = 90 * 24 * 60 * 60 * 1000 | |
| 288 | - // 不能选择未来日期 | |
| 289 | - if (timeMs > nowMs) return true | |
| 290 | - // 若已选择开始时间:结束时间需在 [start, start+90天] 区间内 | |
| 291 | - if (this.query.startTime) { | |
| 292 | - const maxEnd = this.query.startTime + ninetyDaysMs | |
| 293 | - return timeMs < this.query.startTime || timeMs > maxEnd | |
| 294 | - } | |
| 295 | - return false | |
| 296 | - } | |
| 297 | - } | |
| 290 | + // 所有列配置 | |
| 291 | + columnOptions: [ | |
| 292 | + { prop: 'date', label: '开单日期', width: 120, sortable: false, align: 'left' }, | |
| 293 | + { prop: 'customerName', label: '客户姓名', width: 100, sortable: false, align: 'left' }, | |
| 294 | + { prop: 'customerPhone', label: '客户电话', width: 120, sortable: false, align: 'left' }, | |
| 295 | + { prop: 'goldTriangle', label: '金三角', width: 100, sortable: false, align: 'left' }, | |
| 296 | + { prop: 'customerType', label: '客户类型', width: 100, sortable: false, align: 'left' }, | |
| 297 | + { prop: 'purchasedItems', label: '购买项目', minWidth: 200, sortable: false, align: 'left' }, | |
| 298 | + { prop: 'giftedItems', label: '赠送项目', minWidth: 200, sortable: false, align: 'left' }, | |
| 299 | + { prop: 'experienceItems', label: '体验项目', minWidth: 200, sortable: false, align: 'left' }, | |
| 300 | + { prop: 'healthTeachers', label: '健康师', minWidth: 150, sortable: false, align: 'left' }, | |
| 301 | + { prop: 'paidAmount', label: '已付金额', width: 100, sortable: true, align: 'left' }, | |
| 302 | + { prop: 'debtAmount', label: '欠款金额', width: 100, sortable: true, align: 'left' }, | |
| 303 | + { prop: 'totalAmount', label: '总金额', width: 100, sortable: true, align: 'left' }, | |
| 304 | + { prop: 'paymentMethod', label: '支付方式', width: 100, sortable: false, align: 'left' }, | |
| 305 | + { prop: 'remark', label: '备注', minWidth: 150, sortable: false, align: 'left' }, | |
| 306 | + { prop: 'createTime', label: '开单时间', width: 150, sortable: true, align: 'left' } | |
| 307 | + ] | |
| 308 | + } | |
| 309 | + }, | |
| 310 | + computed: { | |
| 311 | + // 可见的列 | |
| 312 | + visibleColumns() { | |
| 313 | + return this.columnOptions.filter(col => this.selectedColumns.includes(col.prop)) | |
| 314 | + }, | |
| 315 | + // 计算表格高度 | |
| 316 | + tableHeight() { | |
| 317 | + // 根据视口高度动态计算,减去筛选条件、统计卡片等的高度 | |
| 318 | + return 'calc(100vh - 400px)' | |
| 298 | 319 | } |
| 299 | 320 | }, |
| 300 | 321 | created() { |
| 301 | 322 | this.initStoreOptions() |
| 302 | 323 | this.setDefaultTimeRange() |
| 324 | + this.initColumnSettings() | |
| 303 | 325 | }, |
| 304 | 326 | methods: { |
| 305 | 327 | // 初始化门店选项 |
| ... | ... | @@ -382,31 +404,6 @@ export default { |
| 382 | 404 | this.setDefaultTimeRange() |
| 383 | 405 | }, |
| 384 | 406 | |
| 385 | - // 导出数据 | |
| 386 | - exportData() { | |
| 387 | - if (!this.query.storeId) { | |
| 388 | - this.$message({ | |
| 389 | - type: 'warning', | |
| 390 | - message: '请先选择门店并查询数据', | |
| 391 | - duration: 1500 | |
| 392 | - }) | |
| 393 | - return | |
| 394 | - } | |
| 395 | - | |
| 396 | - const params = { | |
| 397 | - storeId: this.query.storeId, | |
| 398 | - startTime: this.query.startTime, | |
| 399 | - endTime: this.query.endTime | |
| 400 | - } | |
| 401 | - | |
| 402 | - // 这里可以调用导出接口 | |
| 403 | - this.$message({ | |
| 404 | - type: 'info', | |
| 405 | - message: '导出功能开发中...', | |
| 406 | - duration: 1500 | |
| 407 | - }) | |
| 408 | - }, | |
| 409 | - | |
| 410 | 407 | // 格式化金额 |
| 411 | 408 | formatMoney(amount) { |
| 412 | 409 | if (!amount && amount !== 0) return '0.00' |
| ... | ... | @@ -444,6 +441,96 @@ export default { |
| 444 | 441 | const minutes = String(date.getMinutes()).padStart(2, '0') |
| 445 | 442 | const seconds = String(date.getSeconds()).padStart(2, '0') |
| 446 | 443 | return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}` |
| 444 | + }, | |
| 445 | + | |
| 446 | + // 初始化列设置 | |
| 447 | + initColumnSettings() { | |
| 448 | + // 默认显示所有列 | |
| 449 | + this.selectedColumns = this.columnOptions.map(col => col.prop) | |
| 450 | + // 尝试从本地存储读取 | |
| 451 | + const savedColumns = localStorage.getItem('form1_column_settings') | |
| 452 | + if (savedColumns) { | |
| 453 | + try { | |
| 454 | + const columns = JSON.parse(savedColumns) | |
| 455 | + if (Array.isArray(columns) && columns.length > 0) { | |
| 456 | + this.selectedColumns = columns | |
| 457 | + } | |
| 458 | + } catch (e) { | |
| 459 | + console.error('读取列设置失败:', e) | |
| 460 | + } | |
| 461 | + } | |
| 462 | + }, | |
| 463 | + | |
| 464 | + // 全选列 | |
| 465 | + selectAllColumns() { | |
| 466 | + this.selectedColumns = this.columnOptions.map(col => col.prop) | |
| 467 | + }, | |
| 468 | + | |
| 469 | + // 清空列 | |
| 470 | + clearAllColumns() { | |
| 471 | + this.selectedColumns = [] | |
| 472 | + }, | |
| 473 | + | |
| 474 | + // 保存列设置 | |
| 475 | + saveColumnSettings() { | |
| 476 | + if (this.selectedColumns.length === 0) { | |
| 477 | + this.$message({ | |
| 478 | + type: 'warning', | |
| 479 | + message: '至少需要显示一列', | |
| 480 | + duration: 1500 | |
| 481 | + }) | |
| 482 | + return | |
| 483 | + } | |
| 484 | + // 保存到本地存储 | |
| 485 | + localStorage.setItem('form1_column_settings', JSON.stringify(this.selectedColumns)) | |
| 486 | + this.showColumnDialog = false | |
| 487 | + this.$message({ | |
| 488 | + type: 'success', | |
| 489 | + message: '字段设置已保存', | |
| 490 | + duration: 1500 | |
| 491 | + }) | |
| 492 | + }, | |
| 493 | + | |
| 494 | + // 排序变化 | |
| 495 | + handleSortChange({ column, prop, order }) { | |
| 496 | + this.sortInfo = { | |
| 497 | + prop: prop || '', | |
| 498 | + order: order || '' | |
| 499 | + } | |
| 500 | + this.sortList() | |
| 501 | + }, | |
| 502 | + | |
| 503 | + // 排序列表 | |
| 504 | + sortList() { | |
| 505 | + if (!this.sortInfo.prop || !this.sortInfo.order) { | |
| 506 | + return | |
| 507 | + } | |
| 508 | + | |
| 509 | + const prop = this.sortInfo.prop | |
| 510 | + const order = this.sortInfo.order | |
| 511 | + const isAsc = order === 'ascending' | |
| 512 | + | |
| 513 | + this.records.sort((a, b) => { | |
| 514 | + let aVal = a[prop] | |
| 515 | + let bVal = b[prop] | |
| 516 | + | |
| 517 | + // 处理空值 | |
| 518 | + if (aVal === null || aVal === undefined) aVal = 0 | |
| 519 | + if (bVal === null || bVal === undefined) bVal = 0 | |
| 520 | + | |
| 521 | + // 转换为数字进行比较 | |
| 522 | + aVal = Number(aVal) | |
| 523 | + bVal = Number(bVal) | |
| 524 | + | |
| 525 | + if (isNaN(aVal)) aVal = 0 | |
| 526 | + if (isNaN(bVal)) bVal = 0 | |
| 527 | + | |
| 528 | + if (isAsc) { | |
| 529 | + return aVal - bVal | |
| 530 | + } else { | |
| 531 | + return bVal - aVal | |
| 532 | + } | |
| 533 | + }) | |
| 447 | 534 | } |
| 448 | 535 | } |
| 449 | 536 | } |
| ... | ... | @@ -541,6 +628,34 @@ export default { |
| 541 | 628 | font-weight: 500; |
| 542 | 629 | } |
| 543 | 630 | |
| 631 | +.table-wrapper { | |
| 632 | + height: 100%; | |
| 633 | + overflow: hidden; | |
| 634 | + | |
| 635 | + ::v-deep .el-table { | |
| 636 | + width: 100%; | |
| 637 | + } | |
| 638 | + | |
| 639 | + ::v-deep .el-table__body-wrapper { | |
| 640 | + overflow-y: auto; | |
| 641 | + overflow-x: auto; | |
| 642 | + } | |
| 643 | + | |
| 644 | + ::v-deep .el-table__header-wrapper { | |
| 645 | + overflow-x: auto; | |
| 646 | + overflow-y: hidden; | |
| 647 | + // 隐藏滚动条但保持滚动功能 | |
| 648 | + scrollbar-width: none; // Firefox | |
| 649 | + -ms-overflow-style: none; // IE 和 Edge | |
| 650 | + | |
| 651 | + &::-webkit-scrollbar { | |
| 652 | + display: none; // Chrome, Safari, Opera | |
| 653 | + width: 0; | |
| 654 | + height: 0; | |
| 655 | + } | |
| 656 | + } | |
| 657 | +} | |
| 658 | + | |
| 544 | 659 | // 响应式设计 |
| 545 | 660 | @media (max-width: 768px) { |
| 546 | 661 | .statistics-cards { | ... | ... |
antis-ncc-admin/src/views/statisticsList/form12.vue
| ... | ... | @@ -115,6 +115,23 @@ |
| 115 | 115 | </el-form-item> |
| 116 | 116 | </el-col> |
| 117 | 117 | <el-col :span="6"> |
| 118 | + <el-form-item label="客户类型"> | |
| 119 | + <el-select | |
| 120 | + v-model="query.memberType" | |
| 121 | + placeholder="请选择客户类型" | |
| 122 | + filterable | |
| 123 | + clearable | |
| 124 | + :style='{"width":"100%"}'> | |
| 125 | + <el-option | |
| 126 | + v-for="item in memberTypeOptions" | |
| 127 | + :key="item.id" | |
| 128 | + :label="item.fullName" | |
| 129 | + :value="item.id"> | |
| 130 | + </el-option> | |
| 131 | + </el-select> | |
| 132 | + </el-form-item> | |
| 133 | + </el-col> | |
| 134 | + <el-col :span="6"> | |
| 118 | 135 | <el-form-item> |
| 119 | 136 | <el-button type="primary" icon="el-icon-search" @click="search()">查询</el-button> |
| 120 | 137 | <el-button icon="el-icon-refresh-right" @click="reset()">重置</el-button> |
| ... | ... | @@ -126,6 +143,7 @@ |
| 126 | 143 | <!-- 数据表格 --> |
| 127 | 144 | <div class="NCC-common-layout-main NCC-flex-main"> |
| 128 | 145 | <el-table |
| 146 | + style="overflow-y: scroll;" | |
| 129 | 147 | v-loading="listLoading" |
| 130 | 148 | :data="treeList" |
| 131 | 149 | border |
| ... | ... | @@ -228,12 +246,14 @@ export default { |
| 228 | 246 | BillingUserId: undefined, |
| 229 | 247 | PurchaseItemId: undefined, |
| 230 | 248 | GiftItemId: undefined, |
| 231 | - ExperienceItemId: undefined | |
| 249 | + ExperienceItemId: undefined, | |
| 250 | + memberType: undefined | |
| 232 | 251 | }, |
| 233 | 252 | memberOptions: [], |
| 234 | 253 | memberLoading: false, |
| 235 | 254 | storeOptions: [], |
| 236 | 255 | itemOptions: [], |
| 256 | + memberTypeOptions: [], | |
| 237 | 257 | list: [], |
| 238 | 258 | treeList: [], |
| 239 | 259 | listLoading: false, |
| ... | ... | @@ -249,6 +269,7 @@ export default { |
| 249 | 269 | this.initStoreOptions() |
| 250 | 270 | this.initItemOptions() |
| 251 | 271 | this.initMemberOptions() |
| 272 | + this.getMemberTypeOptions() | |
| 252 | 273 | this.search() |
| 253 | 274 | }, |
| 254 | 275 | methods: { |
| ... | ... | @@ -334,6 +355,28 @@ export default { |
| 334 | 355 | } |
| 335 | 356 | }, |
| 336 | 357 | |
| 358 | + // 初始化客户类型选项 | |
| 359 | + getMemberTypeOptions() { | |
| 360 | + request({ | |
| 361 | + url: '/api/Extend/lqkhxx/deduct-types', | |
| 362 | + method: 'GET', | |
| 363 | + }).then((res) => { | |
| 364 | + if (res.code == 200 && res.data) { | |
| 365 | + this.memberTypeOptions = res.data.map(item => ({ | |
| 366 | + fullName: item.Name, | |
| 367 | + id: item.Value + '', | |
| 368 | + value: item.Value + '', | |
| 369 | + label: item.Name, | |
| 370 | + })) | |
| 371 | + } else { | |
| 372 | + this.memberTypeOptions = [] | |
| 373 | + } | |
| 374 | + }).catch((err) => { | |
| 375 | + console.error('获取客户类型失败:', err) | |
| 376 | + this.memberTypeOptions = [] | |
| 377 | + }) | |
| 378 | + }, | |
| 379 | + | |
| 337 | 380 | // 查询数据 |
| 338 | 381 | search() { |
| 339 | 382 | this.listLoading = true |
| ... | ... | @@ -523,7 +566,8 @@ export default { |
| 523 | 566 | BillingUserId: undefined, |
| 524 | 567 | PurchaseItemId: undefined, |
| 525 | 568 | GiftItemId: undefined, |
| 526 | - ExperienceItemId: undefined | |
| 569 | + ExperienceItemId: undefined, | |
| 570 | + memberType: undefined | |
| 527 | 571 | } |
| 528 | 572 | this.listQuery.currentPage = 1 |
| 529 | 573 | this.listQuery.pageSize = 20 | ... | ... |
antis-ncc-admin/src/views/statisticsList/form13.vue
antis-ncc-admin/src/views/statisticsList/form14.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-date-picker | |
| 10 | + v-model="query.StartConsumeTime" | |
| 11 | + type="date" | |
| 12 | + value-format="yyyy-MM-dd" | |
| 13 | + format="yyyy-MM-dd" | |
| 14 | + placeholder="开始时间" | |
| 15 | + /> | |
| 16 | + </el-form-item> | |
| 17 | + </el-col> | |
| 18 | + <el-col :span="6"> | |
| 19 | + <el-form-item label="结束时间"> | |
| 20 | + <el-date-picker | |
| 21 | + v-model="query.EndConsumeTime" | |
| 22 | + type="date" | |
| 23 | + value-format="yyyy-MM-dd" | |
| 24 | + format="yyyy-MM-dd" | |
| 25 | + placeholder="结束时间" | |
| 26 | + /> | |
| 27 | + </el-form-item> | |
| 28 | + </el-col> | |
| 29 | + <el-col :span="6"> | |
| 30 | + <el-form-item label="品项"> | |
| 31 | + <el-select | |
| 32 | + v-model="query.ItemId" | |
| 33 | + placeholder="请选择品项" | |
| 34 | + filterable | |
| 35 | + clearable | |
| 36 | + :style='{"width":"100%"}'> | |
| 37 | + <el-option | |
| 38 | + v-for="item in itemOptions" | |
| 39 | + :key="item.id" | |
| 40 | + :label="item.xmmc" | |
| 41 | + :value="item.id"> | |
| 42 | + </el-option> | |
| 43 | + </el-select> | |
| 44 | + </el-form-item> | |
| 45 | + </el-col> | |
| 46 | + <el-col :span="6"> | |
| 47 | + <el-form-item label="会员"> | |
| 48 | + <el-select | |
| 49 | + v-model="query.MemberId" | |
| 50 | + placeholder="请选择会员" | |
| 51 | + filterable | |
| 52 | + remote | |
| 53 | + :remote-method="remoteMemberMethod" | |
| 54 | + :loading="memberLoading" | |
| 55 | + clearable | |
| 56 | + :style='{"width":"100%"}'> | |
| 57 | + <el-option | |
| 58 | + v-for="item in memberOptions" | |
| 59 | + :key="item.id" | |
| 60 | + :label="`${item.khmc}(${item.sjh || ''} ${item.gsmdName || ''})`" | |
| 61 | + :value="item.id"> | |
| 62 | + </el-option> | |
| 63 | + </el-select> | |
| 64 | + </el-form-item> | |
| 65 | + </el-col> | |
| 66 | + <el-col :span="6"> | |
| 67 | + <el-form-item label="品项类型"> | |
| 68 | + <el-select | |
| 69 | + v-model="query.ItemType" | |
| 70 | + placeholder="请选择品项类型" | |
| 71 | + filterable | |
| 72 | + clearable | |
| 73 | + :style='{"width":"100%"}'> | |
| 74 | + <el-option | |
| 75 | + v-for="item in itemTypeOptions" | |
| 76 | + :key="item.value" | |
| 77 | + :label="item.label" | |
| 78 | + :value="item.value"> | |
| 79 | + </el-option> | |
| 80 | + </el-select> | |
| 81 | + </el-form-item> | |
| 82 | + </el-col> | |
| 83 | + <el-col :span="6"> | |
| 84 | + <el-form-item label="来源类型"> | |
| 85 | + <el-select | |
| 86 | + v-model="query.SourceType" | |
| 87 | + placeholder="请选择来源类型" | |
| 88 | + clearable | |
| 89 | + :style='{"width":"100%"}'> | |
| 90 | + <el-option label="购买" value="购买" /> | |
| 91 | + <el-option label="赠送" value="赠送" /> | |
| 92 | + <el-option label="体验" value="体验" /> | |
| 93 | + </el-select> | |
| 94 | + </el-form-item> | |
| 95 | + </el-col> | |
| 96 | + <el-col :span="6"> | |
| 97 | + <el-form-item label="门店"> | |
| 98 | + <el-select | |
| 99 | + v-model="query.StoreId" | |
| 100 | + placeholder="请选择门店" | |
| 101 | + filterable | |
| 102 | + clearable | |
| 103 | + :style='{"width":"100%"}'> | |
| 104 | + <el-option | |
| 105 | + v-for="store in storeOptions" | |
| 106 | + :key="store.id" | |
| 107 | + :label="store.dm" | |
| 108 | + :value="store.id"> | |
| 109 | + </el-option> | |
| 110 | + </el-select> | |
| 111 | + </el-form-item> | |
| 112 | + </el-col> | |
| 113 | + <el-col :span="6"> | |
| 114 | + <el-form-item> | |
| 115 | + <el-button type="primary" icon="el-icon-search" @click="search()">查询</el-button> | |
| 116 | + <el-button icon="el-icon-refresh-right" @click="reset()">重置</el-button> | |
| 117 | + </el-form-item> | |
| 118 | + </el-col> | |
| 119 | + </el-form> | |
| 120 | + </el-row> | |
| 121 | + | |
| 122 | + <!-- 数据表格 --> | |
| 123 | + <div class="NCC-common-layout-main NCC-flex-main"> | |
| 124 | + <NCC-table v-loading="listLoading" :data="list" has-c> | |
| 125 | + <el-table-column prop="consumeTime" label="耗卡时间" min-width="120"> | |
| 126 | + <template slot-scope="scope"> | |
| 127 | + <i class="el-icon-time" style="margin-right: 4px; color: #409EFF;"></i> | |
| 128 | + <span>{{ formatDateTime(scope.row.consumeTime) || '无' }}</span> | |
| 129 | + </template> | |
| 130 | + </el-table-column> | |
| 131 | + <el-table-column prop="memberName" label="会员名称" min-width="120"> | |
| 132 | + <template slot-scope="scope"> | |
| 133 | + <i class="el-icon-user" style="margin-right: 4px; color: #409EFF;"></i> | |
| 134 | + <span>{{ scope.row.memberName || '无' }}</span> | |
| 135 | + </template> | |
| 136 | + </el-table-column> | |
| 137 | + <el-table-column prop="memberPhone" label="会员手机号" min-width="120"> | |
| 138 | + <template slot-scope="scope"> | |
| 139 | + <i class="el-icon-phone" style="margin-right: 4px; color: #67C23A;"></i> | |
| 140 | + <span>{{ scope.row.memberPhone || '无' }}</span> | |
| 141 | + </template> | |
| 142 | + </el-table-column> | |
| 143 | + <el-table-column prop="storeName" label="门店名称" min-width="120"> | |
| 144 | + <template slot-scope="scope"> | |
| 145 | + <i class="el-icon-shop" style="margin-right: 4px; color: #409EFF;"></i> | |
| 146 | + <span>{{ scope.row.storeName || '无' }}</span> | |
| 147 | + </template> | |
| 148 | + </el-table-column> | |
| 149 | + <el-table-column prop="itemName" label="品项名称" min-width="150"> | |
| 150 | + <template slot-scope="scope"> | |
| 151 | + <i class="el-icon-goods" style="margin-right: 4px; color: #409EFF;"></i> | |
| 152 | + <span>{{ scope.row.itemName || '无' }}</span> | |
| 153 | + </template> | |
| 154 | + </el-table-column> | |
| 155 | + <el-table-column prop="itemType" label="品项类型" min-width="100"> | |
| 156 | + <template slot-scope="scope"> | |
| 157 | + <el-tag | |
| 158 | + :type="getItemTypeTagType(scope.row.itemType)" | |
| 159 | + size="small"> | |
| 160 | + {{ scope.row.itemType || '无' }} | |
| 161 | + </el-tag> | |
| 162 | + </template> | |
| 163 | + </el-table-column> | |
| 164 | + <el-table-column prop="itemPrice" label="品项金额" min-width="120" align="right"> | |
| 165 | + <template slot-scope="scope"> | |
| 166 | + <i class="el-icon-money" style="margin-right: 4px; color: #67C23A;"></i> | |
| 167 | + <span class="amount-paid">¥{{ formatMoney(scope.row.itemPrice) }}</span> | |
| 168 | + </template> | |
| 169 | + </el-table-column> | |
| 170 | + <el-table-column prop="projectNumber" label="项目数" min-width="100" align="right"> | |
| 171 | + <template slot-scope="scope"> | |
| 172 | + <i class="el-icon-menu" style="margin-right: 4px; color: #67C23A;"></i> | |
| 173 | + <span>{{ scope.row.projectNumber || 0 }}</span> | |
| 174 | + </template> | |
| 175 | + </el-table-column> | |
| 176 | + <el-table-column prop="originalProjectNumber" label="原始项目数" min-width="120" align="right"> | |
| 177 | + <template slot-scope="scope"> | |
| 178 | + <i class="el-icon-document" style="margin-right: 4px; color: #909399;"></i> | |
| 179 | + <span>{{ scope.row.originalProjectNumber || 0 }}</span> | |
| 180 | + </template> | |
| 181 | + </el-table-column> | |
| 182 | + <el-table-column prop="totalPrice" label="总金额" min-width="120" align="right"> | |
| 183 | + <template slot-scope="scope"> | |
| 184 | + <i class="el-icon-coin" style="margin-right: 4px; color: #67C23A;"></i> | |
| 185 | + <span class="amount-paid">¥{{ formatMoney(scope.row.totalPrice) }}</span> | |
| 186 | + </template> | |
| 187 | + </el-table-column> | |
| 188 | + <el-table-column prop="sourceType" label="来源类型" min-width="100"> | |
| 189 | + <template slot-scope="scope"> | |
| 190 | + <i class="el-icon-link" style="margin-right: 4px; color: #909399;"></i> | |
| 191 | + <span>{{ scope.row.sourceType || '无' }}</span> | |
| 192 | + </template> | |
| 193 | + </el-table-column> | |
| 194 | + </NCC-table> | |
| 195 | + <pagination | |
| 196 | + v-show="total > 0" | |
| 197 | + :total="total" | |
| 198 | + :page.sync="listQuery.currentPage" | |
| 199 | + :limit.sync="listQuery.pageSize" | |
| 200 | + @pagination="search" | |
| 201 | + /> | |
| 202 | + </div> | |
| 203 | + </div> | |
| 204 | + </div> | |
| 205 | +</template> | |
| 206 | + | |
| 207 | +<script> | |
| 208 | +import request from '@/utils/request' | |
| 209 | +import Pagination from '@/components/Pagination' | |
| 210 | + | |
| 211 | +export default { | |
| 212 | + name: 'ConsumeItemDetailList', | |
| 213 | + components: { | |
| 214 | + Pagination | |
| 215 | + }, | |
| 216 | + data() { | |
| 217 | + return { | |
| 218 | + query: { | |
| 219 | + StartConsumeTime: undefined, | |
| 220 | + EndConsumeTime: undefined, | |
| 221 | + ItemId: undefined, | |
| 222 | + MemberId: undefined, | |
| 223 | + ItemType: undefined, | |
| 224 | + SourceType: undefined, | |
| 225 | + StoreId: undefined | |
| 226 | + }, | |
| 227 | + itemOptions: [], | |
| 228 | + itemTypeOptions: [], | |
| 229 | + memberOptions: [], | |
| 230 | + memberLoading: false, | |
| 231 | + storeOptions: [], | |
| 232 | + list: [], | |
| 233 | + listLoading: false, | |
| 234 | + total: 0, | |
| 235 | + listQuery: { | |
| 236 | + currentPage: 1, | |
| 237 | + pageSize: 10 | |
| 238 | + } | |
| 239 | + } | |
| 240 | + }, | |
| 241 | + created() { | |
| 242 | + this.setDefaultTimeRange() | |
| 243 | + this.initItemOptions() | |
| 244 | + this.initItemTypeOptions() | |
| 245 | + this.initMemberOptions() | |
| 246 | + this.initStoreOptions() | |
| 247 | + this.search() | |
| 248 | + }, | |
| 249 | + methods: { | |
| 250 | + // 设置默认时间范围(本月1号到现在) | |
| 251 | + setDefaultTimeRange() { | |
| 252 | + const now = new Date() | |
| 253 | + const firstDayOfMonth = new Date(now.getFullYear(), now.getMonth(), 1) | |
| 254 | + | |
| 255 | + this.query.StartConsumeTime = this.formatDate(firstDayOfMonth) | |
| 256 | + this.query.EndConsumeTime = this.formatDate(now) | |
| 257 | + }, | |
| 258 | + | |
| 259 | + // 初始化品项选项 | |
| 260 | + initItemOptions() { | |
| 261 | + request({ | |
| 262 | + url: '/api/Extend/LqXmzl', | |
| 263 | + method: 'GET', | |
| 264 | + data: { | |
| 265 | + currentPage: 1, | |
| 266 | + pageSize: 1000 | |
| 267 | + } | |
| 268 | + }).then(res => { | |
| 269 | + if (res.data && res.data.list) { | |
| 270 | + this.itemOptions = res.data.list | |
| 271 | + } | |
| 272 | + }).catch(() => { | |
| 273 | + this.itemOptions = [] | |
| 274 | + }) | |
| 275 | + }, | |
| 276 | + | |
| 277 | + // 初始化品项类型选项(使用qt2字段数据) | |
| 278 | + initItemTypeOptions() { | |
| 279 | + request({ | |
| 280 | + url: '/api/Extend/lqxmzl/GetDistinctFieldData?fieldName=qt2', | |
| 281 | + method: 'GET' | |
| 282 | + }).then(res => { | |
| 283 | + if (res.code == 200 && res.data && res.data.values) { | |
| 284 | + this.itemTypeOptions = res.data.values.map(item => ({ | |
| 285 | + label: item, | |
| 286 | + value: item | |
| 287 | + })) | |
| 288 | + } else { | |
| 289 | + this.itemTypeOptions = [] | |
| 290 | + } | |
| 291 | + }).catch(() => { | |
| 292 | + this.itemTypeOptions = [] | |
| 293 | + }) | |
| 294 | + }, | |
| 295 | + | |
| 296 | + // 初始化会员选项 | |
| 297 | + initMemberOptions() { | |
| 298 | + request({ | |
| 299 | + url: '/api/Extend/LqKhxx', | |
| 300 | + method: 'GET', | |
| 301 | + data: { | |
| 302 | + currentPage: 1, | |
| 303 | + pageSize: 10 | |
| 304 | + } | |
| 305 | + }).then(res => { | |
| 306 | + if (res.code == 200 && res.data.list && res.data.list.length > 0) { | |
| 307 | + this.memberOptions = res.data.list | |
| 308 | + } | |
| 309 | + }).catch(() => { | |
| 310 | + this.memberOptions = [] | |
| 311 | + }) | |
| 312 | + }, | |
| 313 | + | |
| 314 | + // 初始化门店选项 | |
| 315 | + initStoreOptions() { | |
| 316 | + request({ | |
| 317 | + url: '/api/Extend/LqMdxx', | |
| 318 | + method: 'GET', | |
| 319 | + data: { | |
| 320 | + currentPage: 1, | |
| 321 | + pageSize: 1000 | |
| 322 | + } | |
| 323 | + }).then(res => { | |
| 324 | + if (res.data && res.data.list) { | |
| 325 | + this.storeOptions = res.data.list | |
| 326 | + } | |
| 327 | + }).catch(() => { | |
| 328 | + this.storeOptions = [] | |
| 329 | + }) | |
| 330 | + }, | |
| 331 | + | |
| 332 | + // 会员远程搜索 | |
| 333 | + remoteMemberMethod(query) { | |
| 334 | + if (query !== '') { | |
| 335 | + this.memberLoading = true | |
| 336 | + request({ | |
| 337 | + url: '/api/Extend/LqKhxx', | |
| 338 | + method: 'GET', | |
| 339 | + data: { | |
| 340 | + currentPage: 1, | |
| 341 | + pageSize: 20, | |
| 342 | + keyword: query | |
| 343 | + } | |
| 344 | + }).then(res => { | |
| 345 | + this.memberLoading = false | |
| 346 | + if (res.code == 200 && res.data.list && res.data.list.length > 0) { | |
| 347 | + this.memberOptions = res.data.list | |
| 348 | + } else { | |
| 349 | + this.memberOptions = [] | |
| 350 | + } | |
| 351 | + }).catch(() => { | |
| 352 | + this.memberLoading = false | |
| 353 | + this.memberOptions = [] | |
| 354 | + }) | |
| 355 | + } else { | |
| 356 | + this.memberOptions = [] | |
| 357 | + } | |
| 358 | + }, | |
| 359 | + | |
| 360 | + // 查询数据 | |
| 361 | + search() { | |
| 362 | + this.listLoading = true | |
| 363 | + | |
| 364 | + const params = { | |
| 365 | + currentPage: this.listQuery.currentPage, | |
| 366 | + pageSize: this.listQuery.pageSize | |
| 367 | + } | |
| 368 | + | |
| 369 | + if (this.query.StartConsumeTime) { | |
| 370 | + params.StartConsumeTime = this.query.StartConsumeTime | |
| 371 | + } | |
| 372 | + | |
| 373 | + if (this.query.EndConsumeTime) { | |
| 374 | + params.EndConsumeTime = this.query.EndConsumeTime | |
| 375 | + } | |
| 376 | + | |
| 377 | + if (this.query.ItemId) { | |
| 378 | + params.ItemId = this.query.ItemId | |
| 379 | + } | |
| 380 | + | |
| 381 | + if (this.query.MemberId) { | |
| 382 | + params.MemberId = this.query.MemberId | |
| 383 | + } | |
| 384 | + | |
| 385 | + if (this.query.ItemType) { | |
| 386 | + params.ItemType = this.query.ItemType | |
| 387 | + } | |
| 388 | + | |
| 389 | + if (this.query.SourceType) { | |
| 390 | + params.SourceType = this.query.SourceType | |
| 391 | + } | |
| 392 | + | |
| 393 | + if (this.query.StoreId) { | |
| 394 | + params.StoreId = this.query.StoreId | |
| 395 | + } | |
| 396 | + | |
| 397 | + request({ | |
| 398 | + url: '/api/Extend/lqxhhyhk/consume-item-detail-list', | |
| 399 | + method: 'GET', | |
| 400 | + data: params | |
| 401 | + }).then(res => { | |
| 402 | + if (res.data && res.data.list) { | |
| 403 | + this.list = res.data.list | |
| 404 | + this.total = (res.data.pagination && res.data.pagination.total) || res.data.list.length || 0 | |
| 405 | + } else { | |
| 406 | + this.list = [] | |
| 407 | + this.total = 0 | |
| 408 | + } | |
| 409 | + this.listLoading = false | |
| 410 | + }).catch(err => { | |
| 411 | + console.error('查询失败:', err) | |
| 412 | + this.$message({ | |
| 413 | + type: 'error', | |
| 414 | + message: '查询失败,请重试', | |
| 415 | + duration: 1500 | |
| 416 | + }) | |
| 417 | + this.listLoading = false | |
| 418 | + this.list = [] | |
| 419 | + this.total = 0 | |
| 420 | + }) | |
| 421 | + }, | |
| 422 | + | |
| 423 | + // 重置查询条件 | |
| 424 | + reset() { | |
| 425 | + this.query = { | |
| 426 | + StartConsumeTime: undefined, | |
| 427 | + EndConsumeTime: undefined, | |
| 428 | + ItemId: undefined, | |
| 429 | + MemberId: undefined, | |
| 430 | + ItemType: undefined, | |
| 431 | + SourceType: undefined, | |
| 432 | + StoreId: undefined | |
| 433 | + } | |
| 434 | + this.setDefaultTimeRange() | |
| 435 | + this.listQuery.currentPage = 1 | |
| 436 | + this.listQuery.pageSize = 10 | |
| 437 | + this.list = [] | |
| 438 | + this.total = 0 | |
| 439 | + this.search() | |
| 440 | + }, | |
| 441 | + | |
| 442 | + // 获取品项类型的标签类型 | |
| 443 | + getItemTypeTagType(itemType) { | |
| 444 | + const typeMap = { | |
| 445 | + '生美': 'success', | |
| 446 | + '医美': 'warning', | |
| 447 | + '其他': 'info' | |
| 448 | + } | |
| 449 | + return typeMap[itemType] || '' | |
| 450 | + }, | |
| 451 | + | |
| 452 | + // 格式化金额 | |
| 453 | + formatMoney(amount) { | |
| 454 | + if (!amount && amount !== 0) return '0.00' | |
| 455 | + return Number(amount).toFixed(2) | |
| 456 | + }, | |
| 457 | + | |
| 458 | + // 格式化日期(yyyy-MM-dd) | |
| 459 | + formatDate(date) { | |
| 460 | + if (!date) return '' | |
| 461 | + try { | |
| 462 | + const d = new Date(date) | |
| 463 | + const year = d.getFullYear() | |
| 464 | + const month = String(d.getMonth() + 1).padStart(2, '0') | |
| 465 | + const day = String(d.getDate()).padStart(2, '0') | |
| 466 | + return `${year}-${month}-${day}` | |
| 467 | + } catch (e) { | |
| 468 | + return '' | |
| 469 | + } | |
| 470 | + }, | |
| 471 | + | |
| 472 | + // 格式化日期时间 | |
| 473 | + formatDateTime(dateTime) { | |
| 474 | + if (!dateTime) return '无' | |
| 475 | + try { | |
| 476 | + const date = new Date(dateTime) | |
| 477 | + const year = date.getFullYear() | |
| 478 | + const month = String(date.getMonth() + 1).padStart(2, '0') | |
| 479 | + const day = String(date.getDate()).padStart(2, '0') | |
| 480 | + const hours = String(date.getHours()).padStart(2, '0') | |
| 481 | + const minutes = String(date.getMinutes()).padStart(2, '0') | |
| 482 | + const seconds = String(date.getSeconds()).padStart(2, '0') | |
| 483 | + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}` | |
| 484 | + } catch (e) { | |
| 485 | + return dateTime | |
| 486 | + } | |
| 487 | + } | |
| 488 | + } | |
| 489 | +} | |
| 490 | +</script> | |
| 491 | + | |
| 492 | +<style lang="scss" scoped> | |
| 493 | +.amount-paid { | |
| 494 | + color: #67C23A; | |
| 495 | + font-weight: 500; | |
| 496 | +} | |
| 497 | +</style> | |
| 498 | + | ... | ... |
antis-ncc-admin/src/views/statisticsList/form4.vue
| ... | ... | @@ -54,6 +54,7 @@ |
| 54 | 54 | <el-form-item> |
| 55 | 55 | <el-button type="primary" icon="el-icon-search" @click="search()">查询</el-button> |
| 56 | 56 | <el-button icon="el-icon-refresh-right" @click="reset()">重置</el-button> |
| 57 | + <el-button icon="el-icon-setting" @click="showColumnDialog = true">字段设置</el-button> | |
| 57 | 58 | </el-form-item> |
| 58 | 59 | </el-col> |
| 59 | 60 | </el-form> |
| ... | ... | @@ -110,63 +111,61 @@ |
| 110 | 111 | </div> |
| 111 | 112 | |
| 112 | 113 | <!-- 数据表格 --> |
| 113 | - <div class="NCC-common-layout-main NCC-flex-main"> | |
| 114 | - <NCC-table v-loading="listLoading" :data="list" has-c> | |
| 115 | - <el-table-column prop="ItemNumber" label="品项编号" align="left" width="120" /> | |
| 116 | - <el-table-column prop="ItemName" label="品项名称" align="left" min-width="150"> | |
| 114 | + <div class="NCC-common-layout-main NCC-flex-main table-wrapper"> | |
| 115 | + <el-table | |
| 116 | + v-loading="listLoading" | |
| 117 | + :data="list" | |
| 118 | + border | |
| 119 | + class="NCC-common-table" | |
| 120 | + :height="tableHeight" | |
| 121 | + @sort-change="handleSortChange"> | |
| 122 | + <el-table-column | |
| 123 | + v-for="col in visibleColumns" | |
| 124 | + :key="col.prop" | |
| 125 | + :prop="col.prop" | |
| 126 | + :label="col.label" | |
| 127 | + :width="col.width" | |
| 128 | + :min-width="col.minWidth" | |
| 129 | + :sortable="col.sortable ? 'custom' : false" | |
| 130 | + :align="col.align || 'left'"> | |
| 117 | 131 | <template slot-scope="scope"> |
| 118 | - <span class="item-name-link" @click="handleItemNameClick(scope.row)"> | |
| 132 | + <span v-if="col.prop === 'ItemName'" class="item-name-link" @click="handleItemNameClick(scope.row)"> | |
| 119 | 133 | <i class="el-icon-goods" style="margin-right: 4px; color: #409EFF;"></i> |
| 120 | 134 | {{ scope.row.ItemName || '无' }} |
| 121 | 135 | </span> |
| 136 | + <span v-else-if="col.prop === 'BillingAmount'" class="amount-value"> | |
| 137 | + ¥{{ formatMoney(scope.row.BillingAmount) }} | |
| 138 | + </span> | |
| 139 | + <span v-else-if="col.prop === 'BillingAmountRatio'"> | |
| 140 | + {{ formatPercent(scope.row.BillingAmountRatio) }}% | |
| 141 | + </span> | |
| 142 | + <span v-else-if="col.prop === 'ItemRatio'"> | |
| 143 | + {{ formatPercent(scope.row.ItemRatio) }}% | |
| 144 | + </span> | |
| 145 | + <span v-else-if="col.prop === 'RepeatBuyRate'"> | |
| 146 | + {{ formatPercent(scope.row.RepeatBuyRate) }}% | |
| 147 | + </span> | |
| 148 | + <span v-else-if="col.prop === 'ConsumeAmount'" class="amount-paid"> | |
| 149 | + ¥{{ formatMoney(scope.row.ConsumeAmount) }} | |
| 150 | + </span> | |
| 151 | + <span v-else-if="col.prop === 'ConsumeAmountRatio'"> | |
| 152 | + {{ formatPercent(scope.row.ConsumeAmountRatio) }}% | |
| 153 | + </span> | |
| 154 | + <span v-else-if="col.prop === 'RefundAmount'" class="amount-debt"> | |
| 155 | + ¥{{ formatMoney(scope.row.RefundAmount) }} | |
| 156 | + </span> | |
| 157 | + <span v-else-if="col.prop === 'RefundCount'"> | |
| 158 | + {{ scope.row.RefundCount || 0 }} | |
| 159 | + </span> | |
| 160 | + <span v-else> | |
| 161 | + {{ scope.row[col.prop] !== null && scope.row[col.prop] !== undefined ? scope.row[col.prop] : '无' }} | |
| 162 | + </span> | |
| 122 | 163 | </template> |
| 123 | 164 | </el-table-column> |
| 124 | - <el-table-column prop="BillingAmount" label="开单金额" align="right" width="120"> | |
| 125 | - <template slot-scope="scope"> | |
| 126 | - <span class="amount-value">¥{{ formatMoney(scope.row.BillingAmount) }}</span> | |
| 127 | - </template> | |
| 128 | - </el-table-column> | |
| 129 | - <el-table-column prop="BillingAmountRatio" label="开单占比" align="right" width="110"> | |
| 130 | - <template slot-scope="scope"> | |
| 131 | - {{ formatPercent(scope.row.BillingAmountRatio) }}% | |
| 132 | - </template> | |
| 133 | - </el-table-column> | |
| 134 | - <el-table-column prop="TotalBuyers" label="购买人数" align="right" width="100" /> | |
| 135 | - <el-table-column prop="ItemRatio" label="人次占比" align="right" width="110"> | |
| 136 | - <template slot-scope="scope"> | |
| 137 | - {{ formatPercent(scope.row.ItemRatio) }}% | |
| 138 | - </template> | |
| 139 | - </el-table-column> | |
| 140 | - <el-table-column prop="RepeatBuyers" label="复购人数" align="right" width="100" /> | |
| 141 | - <el-table-column prop="RepeatBuyRate" label="复购率" align="right" width="100"> | |
| 142 | - <template slot-scope="scope"> | |
| 143 | - {{ formatPercent(scope.row.RepeatBuyRate) }}% | |
| 144 | - </template> | |
| 145 | - </el-table-column> | |
| 146 | - <el-table-column prop="ConsumeAmount" label="耗卡金额" align="right" width="120"> | |
| 147 | - <template slot-scope="scope"> | |
| 148 | - <span class="amount-paid">¥{{ formatMoney(scope.row.ConsumeAmount) }}</span> | |
| 149 | - </template> | |
| 150 | - </el-table-column> | |
| 151 | - <el-table-column prop="ConsumeAmountRatio" label="耗卡占比" align="right" width="110"> | |
| 152 | - <template slot-scope="scope"> | |
| 153 | - {{ formatPercent(scope.row.ConsumeAmountRatio) }}% | |
| 154 | - </template> | |
| 155 | - </el-table-column> | |
| 156 | - <el-table-column prop="ConsumePurchaseCount" label="购买耗卡次数" align="right" width="140" /> | |
| 157 | - <el-table-column prop="ConsumeGiftCount" label="赠送耗卡次数" align="right" width="140" /> | |
| 158 | - <el-table-column prop="ConsumeExperienceCount" label="体验耗卡次数" align="right" width="140" /> | |
| 159 | - <el-table-column prop="RefundAmount" label="退款金额" align="right" width="120"> | |
| 160 | - <template slot-scope="scope"> | |
| 161 | - <span class="amount-debt">¥{{ formatMoney(scope.row.RefundAmount) }}</span> | |
| 162 | - </template> | |
| 163 | - </el-table-column> | |
| 164 | - <el-table-column prop="RefundCount" label="退款次数" align="right" width="100"> | |
| 165 | - <template slot-scope="scope"> | |
| 166 | - {{ scope.row.RefundCount || 0 }} | |
| 167 | - </template> | |
| 168 | - </el-table-column> | |
| 169 | - </NCC-table> | |
| 165 | + <template slot="empty"> | |
| 166 | + <el-empty description="暂无数据" :image-size="120"></el-empty> | |
| 167 | + </template> | |
| 168 | + </el-table> | |
| 170 | 169 | </div> |
| 171 | 170 | </div> |
| 172 | 171 | |
| ... | ... | @@ -178,6 +177,29 @@ |
| 178 | 177 | :end-time="query.endTime" |
| 179 | 178 | @close="dialogVisible = false" |
| 180 | 179 | /> |
| 180 | + | |
| 181 | + <!-- 字段设置弹窗 --> | |
| 182 | + <el-dialog title="字段设置" :visible.sync="showColumnDialog" width="500px" append-to-body> | |
| 183 | + <div class="column-settings"> | |
| 184 | + <div class="column-actions" style="margin-bottom: 15px;"> | |
| 185 | + <el-button size="small" @click="selectAllColumns">全选</el-button> | |
| 186 | + <el-button size="small" @click="clearAllColumns">清空</el-button> | |
| 187 | + </div> | |
| 188 | + <el-checkbox-group v-model="selectedColumns"> | |
| 189 | + <el-checkbox | |
| 190 | + v-for="col in columnOptions" | |
| 191 | + :key="col.prop" | |
| 192 | + :label="col.prop" | |
| 193 | + style="display: block; margin-bottom: 10px;"> | |
| 194 | + {{ col.label }} | |
| 195 | + </el-checkbox> | |
| 196 | + </el-checkbox-group> | |
| 197 | + </div> | |
| 198 | + <div slot="footer" class="dialog-footer"> | |
| 199 | + <el-button @click="showColumnDialog = false">取消</el-button> | |
| 200 | + <el-button type="primary" @click="saveColumnSettings">确定</el-button> | |
| 201 | + </div> | |
| 202 | + </el-dialog> | |
| 181 | 203 | </div> |
| 182 | 204 | </template> |
| 183 | 205 | |
| ... | ... | @@ -204,13 +226,49 @@ export default { |
| 204 | 226 | list: [], |
| 205 | 227 | listLoading: false, |
| 206 | 228 | dialogVisible: false, |
| 207 | - currentItemId: null | |
| 229 | + currentItemId: null, | |
| 230 | + showColumnDialog: false, | |
| 231 | + selectedColumns: [], | |
| 232 | + sortInfo: { | |
| 233 | + prop: '', | |
| 234 | + order: '' | |
| 235 | + }, | |
| 236 | + // 所有列配置 | |
| 237 | + columnOptions: [ | |
| 238 | + { prop: 'ItemNumber', label: '品项编号', width: 120, sortable: true, align: 'left' }, | |
| 239 | + { prop: 'ItemName', label: '品项名称', minWidth: 150, sortable: true, align: 'left', slot: true }, | |
| 240 | + { prop: 'BillingAmount', label: '开单金额', width: 120, sortable: true, align: 'right', slot: true }, | |
| 241 | + { prop: 'BillingAmountRatio', label: '开单占比', width: 110, sortable: true, align: 'right', slot: true }, | |
| 242 | + { prop: 'TotalBuyers', label: '购买人数', width: 100, sortable: true, align: 'right' }, | |
| 243 | + { prop: 'ItemRatio', label: '人次占比', width: 110, sortable: true, align: 'right', slot: true }, | |
| 244 | + { prop: 'RepeatBuyers', label: '复购人数', width: 100, sortable: true, align: 'right' }, | |
| 245 | + { prop: 'RepeatBuyRate', label: '复购率', width: 100, sortable: true, align: 'right', slot: true }, | |
| 246 | + { prop: 'ConsumeAmount', label: '耗卡金额', width: 120, sortable: true, align: 'right', slot: true }, | |
| 247 | + { prop: 'ConsumeAmountRatio', label: '耗卡占比', width: 110, sortable: true, align: 'right', slot: true }, | |
| 248 | + { prop: 'ConsumePurchaseCount', label: '购买耗卡次数', width: 140, sortable: true, align: 'right' }, | |
| 249 | + { prop: 'ConsumeGiftCount', label: '赠送耗卡次数', width: 140, sortable: true, align: 'right' }, | |
| 250 | + { prop: 'ConsumeExperienceCount', label: '体验耗卡次数', width: 140, sortable: true, align: 'right' }, | |
| 251 | + { prop: 'RefundAmount', label: '退款金额', width: 120, sortable: true, align: 'right', slot: true }, | |
| 252 | + { prop: 'RefundCount', label: '退款次数', width: 100, sortable: true, align: 'right', slot: true } | |
| 253 | + ] | |
| 254 | + } | |
| 255 | + }, | |
| 256 | + computed: { | |
| 257 | + // 可见的列 | |
| 258 | + visibleColumns() { | |
| 259 | + return this.columnOptions.filter(col => this.selectedColumns.includes(col.prop)) | |
| 260 | + }, | |
| 261 | + // 计算表格高度 | |
| 262 | + tableHeight() { | |
| 263 | + // 根据视口高度动态计算,减去筛选条件、统计卡片等的高度 | |
| 264 | + return 'calc(100vh - 400px)' | |
| 208 | 265 | } |
| 209 | 266 | }, |
| 210 | 267 | created() { |
| 211 | 268 | this.setDefaultTimeRange() |
| 212 | 269 | this.initStoreOptions() |
| 213 | 270 | this.initItemOptions() |
| 271 | + this.initColumnSettings() | |
| 214 | 272 | this.search() |
| 215 | 273 | }, |
| 216 | 274 | methods: { |
| ... | ... | @@ -379,6 +437,96 @@ export default { |
| 379 | 437 | |
| 380 | 438 | this.currentItemId = row.ItemId |
| 381 | 439 | this.dialogVisible = true |
| 440 | + }, | |
| 441 | + | |
| 442 | + // 初始化列设置 | |
| 443 | + initColumnSettings() { | |
| 444 | + // 默认显示所有列 | |
| 445 | + this.selectedColumns = this.columnOptions.map(col => col.prop) | |
| 446 | + // 尝试从本地存储读取 | |
| 447 | + const savedColumns = localStorage.getItem('form4_column_settings') | |
| 448 | + if (savedColumns) { | |
| 449 | + try { | |
| 450 | + const columns = JSON.parse(savedColumns) | |
| 451 | + if (Array.isArray(columns) && columns.length > 0) { | |
| 452 | + this.selectedColumns = columns | |
| 453 | + } | |
| 454 | + } catch (e) { | |
| 455 | + console.error('读取列设置失败:', e) | |
| 456 | + } | |
| 457 | + } | |
| 458 | + }, | |
| 459 | + | |
| 460 | + // 全选列 | |
| 461 | + selectAllColumns() { | |
| 462 | + this.selectedColumns = this.columnOptions.map(col => col.prop) | |
| 463 | + }, | |
| 464 | + | |
| 465 | + // 清空列 | |
| 466 | + clearAllColumns() { | |
| 467 | + this.selectedColumns = [] | |
| 468 | + }, | |
| 469 | + | |
| 470 | + // 保存列设置 | |
| 471 | + saveColumnSettings() { | |
| 472 | + if (this.selectedColumns.length === 0) { | |
| 473 | + this.$message({ | |
| 474 | + type: 'warning', | |
| 475 | + message: '至少需要显示一列', | |
| 476 | + duration: 1500 | |
| 477 | + }) | |
| 478 | + return | |
| 479 | + } | |
| 480 | + // 保存到本地存储 | |
| 481 | + localStorage.setItem('form4_column_settings', JSON.stringify(this.selectedColumns)) | |
| 482 | + this.showColumnDialog = false | |
| 483 | + this.$message({ | |
| 484 | + type: 'success', | |
| 485 | + message: '字段设置已保存', | |
| 486 | + duration: 1500 | |
| 487 | + }) | |
| 488 | + }, | |
| 489 | + | |
| 490 | + // 排序变化 | |
| 491 | + handleSortChange({ column, prop, order }) { | |
| 492 | + this.sortInfo = { | |
| 493 | + prop: prop || '', | |
| 494 | + order: order || '' | |
| 495 | + } | |
| 496 | + this.sortList() | |
| 497 | + }, | |
| 498 | + | |
| 499 | + // 排序列表 | |
| 500 | + sortList() { | |
| 501 | + if (!this.sortInfo.prop || !this.sortInfo.order) { | |
| 502 | + return | |
| 503 | + } | |
| 504 | + | |
| 505 | + const prop = this.sortInfo.prop | |
| 506 | + const order = this.sortInfo.order | |
| 507 | + const isAsc = order === 'ascending' | |
| 508 | + | |
| 509 | + this.list.sort((a, b) => { | |
| 510 | + let aVal = a[prop] | |
| 511 | + let bVal = b[prop] | |
| 512 | + | |
| 513 | + // 处理空值 | |
| 514 | + if (aVal === null || aVal === undefined) aVal = 0 | |
| 515 | + if (bVal === null || bVal === undefined) bVal = 0 | |
| 516 | + | |
| 517 | + // 转换为数字进行比较 | |
| 518 | + aVal = Number(aVal) | |
| 519 | + bVal = Number(bVal) | |
| 520 | + | |
| 521 | + if (isNaN(aVal)) aVal = 0 | |
| 522 | + if (isNaN(bVal)) bVal = 0 | |
| 523 | + | |
| 524 | + if (isAsc) { | |
| 525 | + return aVal - bVal | |
| 526 | + } else { | |
| 527 | + return bVal - aVal | |
| 528 | + } | |
| 529 | + }) | |
| 382 | 530 | } |
| 383 | 531 | } |
| 384 | 532 | } |
| ... | ... | @@ -456,5 +604,33 @@ export default { |
| 456 | 604 | text-decoration: underline; |
| 457 | 605 | } |
| 458 | 606 | } |
| 607 | + | |
| 608 | +.table-wrapper { | |
| 609 | + height: 100%; | |
| 610 | + overflow: hidden; | |
| 611 | + | |
| 612 | + ::v-deep .el-table { | |
| 613 | + width: 100%; | |
| 614 | + } | |
| 615 | + | |
| 616 | + ::v-deep .el-table__body-wrapper { | |
| 617 | + overflow-y: auto; | |
| 618 | + overflow-x: auto; | |
| 619 | + } | |
| 620 | + | |
| 621 | + ::v-deep .el-table__header-wrapper { | |
| 622 | + overflow-x: auto; | |
| 623 | + overflow-y: hidden; | |
| 624 | + // 隐藏滚动条但保持滚动功能 | |
| 625 | + scrollbar-width: none; // Firefox | |
| 626 | + -ms-overflow-style: none; // IE 和 Edge | |
| 627 | + | |
| 628 | + &::-webkit-scrollbar { | |
| 629 | + display: none; // Chrome, Safari, Opera | |
| 630 | + width: 0; | |
| 631 | + height: 0; | |
| 632 | + } | |
| 633 | + } | |
| 634 | +} | |
| 459 | 635 | </style> |
| 460 | 636 | ... | ... |
绿纤uni-app/apis/modules/laundry-flow.js
0 → 100644
| 1 | +import request from '@/service/request.js' | |
| 2 | +import config from '@/common/config.js' | |
| 3 | + | |
| 4 | +export default { | |
| 5 | + // 获取毛巾流水列表 | |
| 6 | + getLaundryFlowList(params) { | |
| 7 | + return request.get(`${config.getApiBaseUrl()}/api/Extend/LqLaundryFlow/GetList`, params); | |
| 8 | + }, | |
| 9 | + | |
| 10 | + // 创建送出记录 | |
| 11 | + createSendRecord(data) { | |
| 12 | + return request.post(`${config.getApiBaseUrl()}/api/Extend/LqLaundryFlow/Send`, data); | |
| 13 | + }, | |
| 14 | + | |
| 15 | + // 创建送回记录 | |
| 16 | + createReturnRecord(data) { | |
| 17 | + return request.post(`${config.getApiBaseUrl()}/api/Extend/LqLaundryFlow/Return`, data); | |
| 18 | + }, | |
| 19 | + | |
| 20 | + // 获取清洗商列表 | |
| 21 | + getSupplierList(params) { | |
| 22 | + return request.get(`${config.getApiBaseUrl()}/api/Extend/LqLaundrySupplier/GetList`, params); | |
| 23 | + }, | |
| 24 | + | |
| 25 | + // 获取毛巾流水详情 | |
| 26 | + getLaundryFlowDetail(id) { | |
| 27 | + return request.get(`${config.getApiBaseUrl()}/api/Extend/LqLaundryFlow/${id}`); | |
| 28 | + }, | |
| 29 | + | |
| 30 | + // 获取产品类型列表 | |
| 31 | + getProductTypeList() { | |
| 32 | + return request.get(`${config.getApiBaseUrl()}/api/Extend/LqStoreConsumableInventory/consumable-product-type`); | |
| 33 | + } | |
| 34 | +} | |
| 35 | + | ... | ... |
绿纤uni-app/apis/modules/usage.js
0 → 100644
| 1 | +import request from '@/service/request.js' | |
| 2 | +import config from '@/common/config.js' | |
| 3 | + | |
| 4 | +export default { | |
| 5 | + // 获取使用记录列表 | |
| 6 | + getUsageList(params) { | |
| 7 | + return request.get(`${config.getApiBaseUrl()}/api/Extend/LqInventoryUsage/GetList`, params); | |
| 8 | + }, | |
| 9 | + | |
| 10 | + // 批量创建使用记录 | |
| 11 | + batchCreateUsage(data) { | |
| 12 | + return request.post(`${config.getApiBaseUrl()}/api/Extend/LqInventoryUsage/BatchCreate`, data); | |
| 13 | + }, | |
| 14 | + | |
| 15 | + // 作废使用记录 | |
| 16 | + cancelUsage(id, remarks) { | |
| 17 | + return request.put(`${config.getApiBaseUrl()}/api/Extend/LqInventoryUsage/Cancel`, { | |
| 18 | + id: id, | |
| 19 | + remarks: remarks || '' | |
| 20 | + }); | |
| 21 | + } | |
| 22 | +} | |
| 23 | + | ... | ... |
绿纤uni-app/common/config.js
| ... | ... | @@ -10,8 +10,8 @@ const ENV_CONFIG = { |
| 10 | 10 | // 正式环境 |
| 11 | 11 | production: { |
| 12 | 12 | name: '正式环境', |
| 13 | - // apiBaseUrl: 'http://erp_test.lvqianmeiye.com', | |
| 14 | - apiBaseUrl: 'https://erp.lvqianmeiye.com', | |
| 13 | + apiBaseUrl: 'http://erp_test.lvqianmeiye.com', | |
| 14 | + // apiBaseUrl: 'https://erp.lvqianmeiye.com', | |
| 15 | 15 | description: '生产环境服务器' |
| 16 | 16 | } |
| 17 | 17 | }; | ... | ... |
绿纤uni-app/pages.json
| ... | ... | @@ -244,6 +244,48 @@ |
| 244 | 244 | { |
| 245 | 245 | "navigationBarTitleText" : "报销申请详情" |
| 246 | 246 | } |
| 247 | + }, | |
| 248 | + { | |
| 249 | + "path" : "pages/usage-list/usage-list", | |
| 250 | + "style" : | |
| 251 | + { | |
| 252 | + "navigationBarTitleText" : "使用记录" | |
| 253 | + } | |
| 254 | + }, | |
| 255 | + { | |
| 256 | + "path" : "pages/usage-form/usage-form", | |
| 257 | + "style" : | |
| 258 | + { | |
| 259 | + "navigationBarTitleText" : "添加使用记录" | |
| 260 | + } | |
| 261 | + }, | |
| 262 | + { | |
| 263 | + "path" : "pages/laundry-flow-list/laundry-flow-list", | |
| 264 | + "style" : | |
| 265 | + { | |
| 266 | + "navigationBarTitleText" : "毛巾记录" | |
| 267 | + } | |
| 268 | + }, | |
| 269 | + { | |
| 270 | + "path" : "pages/laundry-flow-send/laundry-flow-send", | |
| 271 | + "style" : | |
| 272 | + { | |
| 273 | + "navigationBarTitleText" : "创建送出记录" | |
| 274 | + } | |
| 275 | + }, | |
| 276 | + { | |
| 277 | + "path" : "pages/laundry-flow-return/laundry-flow-return", | |
| 278 | + "style" : | |
| 279 | + { | |
| 280 | + "navigationBarTitleText" : "创建送回记录" | |
| 281 | + } | |
| 282 | + }, | |
| 283 | + { | |
| 284 | + "path" : "pages/laundry-flow-detail/laundry-flow-detail", | |
| 285 | + "style" : | |
| 286 | + { | |
| 287 | + "navigationBarTitleText" : "毛巾记录详情" | |
| 288 | + } | |
| 247 | 289 | } |
| 248 | 290 | ], |
| 249 | 291 | "globalStyle": { | ... | ... |
绿纤uni-app/pages/clue-list/clue-list.vue
| ... | ... | @@ -135,6 +135,16 @@ |
| 135 | 135 | > |
| 136 | 136 | 剩余权益 |
| 137 | 137 | </button> |
| 138 | + <!-- <button | |
| 139 | + type="warning" | |
| 140 | + size="small" | |
| 141 | + class="action-btn remaining-rights-btn" | |
| 142 | + @click.stop="goToServiceLog(clue)" | |
| 143 | + > | |
| 144 | + 日志 | |
| 145 | + </button> --> | |
| 146 | + <view></view> | |
| 147 | + <view></view> | |
| 138 | 148 | </view> |
| 139 | 149 | </view> |
| 140 | 150 | </view> |
| ... | ... | @@ -315,7 +325,13 @@ |
| 315 | 325 | this.loadCustomerData(true); |
| 316 | 326 | }, 500); |
| 317 | 327 | }, |
| 318 | - | |
| 328 | + // 跳转到服务日志页面 | |
| 329 | + goToServiceLog(item) { | |
| 330 | + console.log('跳转到服务日志:', item) | |
| 331 | + uni.navigateTo({ | |
| 332 | + url: `/pages/serviceDiary/serviceDiary?consumeId=${item.id}&memberName=${encodeURIComponent(item.name || '未知会员')}` | |
| 333 | + }) | |
| 334 | + }, | |
| 319 | 335 | // 跨店开关变化 |
| 320 | 336 | onCrossStoreChange() { |
| 321 | 337 | // 重置分页状态 | ... | ... |
绿纤uni-app/pages/index/index.vue
| ... | ... | @@ -128,6 +128,18 @@ |
| 128 | 128 | </view> |
| 129 | 129 | <view class="icon-label">报销申请</view> |
| 130 | 130 | </view> |
| 131 | + <view class="icon-btn" @click="goToPage('/pages/usage-list/usage-list')"> | |
| 132 | + <view class="icon"> | |
| 133 | + <u-icon name="list-dot" size="32" color="#43a047"></u-icon> | |
| 134 | + </view> | |
| 135 | + <view class="icon-label">使用记录</view> | |
| 136 | + </view> | |
| 137 | + <view class="icon-btn" @click="goToPage('/pages/laundry-flow-list/laundry-flow-list')"> | |
| 138 | + <view class="icon"> | |
| 139 | + <u-icon name="list-dot" size="32" color="#43a047"></u-icon> | |
| 140 | + </view> | |
| 141 | + <view class="icon-label">毛巾记录</view> | |
| 142 | + </view> | |
| 131 | 143 | <view class="icon-btn" @click="goToPage('/pages/web/web')"> |
| 132 | 144 | <view class="icon"> |
| 133 | 145 | <u-icon name="kefu-ermai" size="32" color="#43a047"></u-icon> | ... | ... |
绿纤uni-app/pages/laundry-flow-detail/laundry-flow-detail.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="container"> | |
| 3 | + <!-- 详情卡片 --> | |
| 4 | + <view class="detail-card"> | |
| 5 | + <view class="card-header">毛巾记录详情</view> | |
| 6 | + <view class="detail-content"> | |
| 7 | + <!-- 加载状态 --> | |
| 8 | + <view v-if="loading" class="loading">正在加载详情...</view> | |
| 9 | + | |
| 10 | + <!-- 错误状态 --> | |
| 11 | + <view v-else-if="error" class="error-state"> | |
| 12 | + <view>{{ error }}</view> | |
| 13 | + <u-button class="retry-btn" @click="retryLoad" type="primary" size="small">重试</u-button> | |
| 14 | + </view> | |
| 15 | + | |
| 16 | + <!-- 详情内容 --> | |
| 17 | + <view v-else-if="detailData" class="info-sections"> | |
| 18 | + <!-- 基本信息 --> | |
| 19 | + <view class="info-section"> | |
| 20 | + <view class="section-title"> | |
| 21 | + <text class="section-icon">📋</text> | |
| 22 | + <text>基本信息</text> | |
| 23 | + </view> | |
| 24 | + <view class="info-grid"> | |
| 25 | + <view class="info-item"> | |
| 26 | + <text class="info-label">流水类型</text> | |
| 27 | + <view class="flow-type-badge" :class="detailData.flowType === 0 ? 'send-type' : 'return-type'"> | |
| 28 | + <text class="flow-type-icon">{{ detailData.flowType === 0 ? '📤' : '📥' }}</text> | |
| 29 | + <text class="flow-type-text">{{ detailData.flowTypeName || '无' }}</text> | |
| 30 | + </view> | |
| 31 | + </view> | |
| 32 | + <view class="info-item"> | |
| 33 | + <text class="info-label">批次号</text> | |
| 34 | + <text class="info-value">{{ detailData.batchNumber || '无' }}</text> | |
| 35 | + </view> | |
| 36 | + <view class="info-item"> | |
| 37 | + <text class="info-label">门店名称</text> | |
| 38 | + <text class="info-value">{{ detailData.storeName || '无' }}</text> | |
| 39 | + </view> | |
| 40 | + <view class="info-item"> | |
| 41 | + <text class="info-label">产品类型</text> | |
| 42 | + <text class="info-value">{{ detailData.productType || '无' }}</text> | |
| 43 | + </view> | |
| 44 | + <view class="info-item"> | |
| 45 | + <text class="info-label">清洗商名称</text> | |
| 46 | + <text class="info-value">{{ detailData.laundrySupplierName || '无' }}</text> | |
| 47 | + </view> | |
| 48 | + <view class="info-item"> | |
| 49 | + <text class="info-label">数量</text> | |
| 50 | + <text class="info-value">{{ detailData.quantity || 0 }}</text> | |
| 51 | + </view> | |
| 52 | + </view> | |
| 53 | + </view> | |
| 54 | + | |
| 55 | + <!-- 费用信息 --> | |
| 56 | + <view class="info-section"> | |
| 57 | + <view class="section-title"> | |
| 58 | + <text class="section-icon">💰</text> | |
| 59 | + <text>费用信息</text> | |
| 60 | + </view> | |
| 61 | + <view class="info-grid"> | |
| 62 | + <view class="info-item"> | |
| 63 | + <text class="info-label">清洗单价</text> | |
| 64 | + <text class="info-value amount">¥{{ detailData.laundryPrice || 0 }}</text> | |
| 65 | + </view> | |
| 66 | + <view class="info-item"> | |
| 67 | + <text class="info-label">总费用</text> | |
| 68 | + <text class="info-value amount total">¥{{ detailData.totalPrice || 0 }}</text> | |
| 69 | + </view> | |
| 70 | + </view> | |
| 71 | + </view> | |
| 72 | + | |
| 73 | + <!-- 其他信息 --> | |
| 74 | + <view class="info-section"> | |
| 75 | + <view class="section-title"> | |
| 76 | + <text class="section-icon">📝</text> | |
| 77 | + <text>其他信息</text> | |
| 78 | + </view> | |
| 79 | + <view class="info-grid"> | |
| 80 | + <view class="info-item full-width"> | |
| 81 | + <text class="info-label">备注</text> | |
| 82 | + <text class="info-value">{{ detailData.remark || '无' }}</text> | |
| 83 | + </view> | |
| 84 | + <view class="info-item"> | |
| 85 | + <text class="info-label">是否有效</text> | |
| 86 | + <view class="status-badge" :class="detailData.isEffective === 1 ? 'effective' : 'ineffective'"> | |
| 87 | + {{ detailData.isEffective === 1 ? '有效' : '无效' }} | |
| 88 | + </view> | |
| 89 | + </view> | |
| 90 | + <view class="info-item"> | |
| 91 | + <text class="info-label">创建人</text> | |
| 92 | + <text class="info-value">{{ detailData.createUserName || '无' }}</text> | |
| 93 | + </view> | |
| 94 | + <view class="info-item"> | |
| 95 | + <text class="info-label">创建时间</text> | |
| 96 | + <text class="info-value">{{ formatTime(detailData.createTime) }}</text> | |
| 97 | + </view> | |
| 98 | + </view> | |
| 99 | + </view> | |
| 100 | + </view> | |
| 101 | + </view> | |
| 102 | + </view> | |
| 103 | + </view> | |
| 104 | +</template> | |
| 105 | + | |
| 106 | +<script> | |
| 107 | + import laundryFlowApi from '@/apis/modules/laundry-flow.js' | |
| 108 | + | |
| 109 | + export default { | |
| 110 | + data() { | |
| 111 | + return { | |
| 112 | + loading: false, | |
| 113 | + error: null, | |
| 114 | + detailData: null, | |
| 115 | + flowId: null | |
| 116 | + } | |
| 117 | + }, | |
| 118 | + | |
| 119 | + onLoad(options) { | |
| 120 | + if (options.id) { | |
| 121 | + this.flowId = options.id | |
| 122 | + this.loadDetail() | |
| 123 | + } else { | |
| 124 | + this.error = '缺少记录ID' | |
| 125 | + } | |
| 126 | + }, | |
| 127 | + | |
| 128 | + methods: { | |
| 129 | + // 加载详情数据 | |
| 130 | + async loadDetail() { | |
| 131 | + if (!this.flowId) return | |
| 132 | + | |
| 133 | + try { | |
| 134 | + this.loading = true | |
| 135 | + this.error = null | |
| 136 | + | |
| 137 | + const res = await laundryFlowApi.getLaundryFlowDetail(this.flowId) | |
| 138 | + | |
| 139 | + if (res.code === 200 && res.data) { | |
| 140 | + this.detailData = res.data | |
| 141 | + } else { | |
| 142 | + this.error = res.msg || res.message || '加载详情失败' | |
| 143 | + } | |
| 144 | + } catch (error) { | |
| 145 | + console.error('加载详情失败:', error) | |
| 146 | + this.error = '加载详情失败,请重试' | |
| 147 | + } finally { | |
| 148 | + this.loading = false | |
| 149 | + } | |
| 150 | + }, | |
| 151 | + | |
| 152 | + // 重试加载 | |
| 153 | + retryLoad() { | |
| 154 | + this.loadDetail() | |
| 155 | + }, | |
| 156 | + | |
| 157 | + // 格式化时间 | |
| 158 | + formatTime(timestamp) { | |
| 159 | + if (!timestamp) return '无' | |
| 160 | + const date = new Date(timestamp) | |
| 161 | + return date.toLocaleString('zh-CN', { | |
| 162 | + year: 'numeric', | |
| 163 | + month: '2-digit', | |
| 164 | + day: '2-digit', | |
| 165 | + hour: '2-digit', | |
| 166 | + minute: '2-digit', | |
| 167 | + second: '2-digit' | |
| 168 | + }) | |
| 169 | + } | |
| 170 | + } | |
| 171 | + } | |
| 172 | +</script> | |
| 173 | + | |
| 174 | +<style lang="scss" scoped> | |
| 175 | + .container { | |
| 176 | + min-height: 100vh; | |
| 177 | + background: linear-gradient(135deg, #e8f5e9 0%, #b2dfdb 100%); | |
| 178 | + padding: 40rpx; | |
| 179 | + box-sizing: border-box; | |
| 180 | + } | |
| 181 | + | |
| 182 | + .detail-card { | |
| 183 | + background: #fff; | |
| 184 | + border-radius: 32rpx; | |
| 185 | + box-shadow: 0 8rpx 32rpx rgba(76, 175, 80, 0.1); | |
| 186 | + overflow: hidden; | |
| 187 | + } | |
| 188 | + | |
| 189 | + .card-header { | |
| 190 | + background: linear-gradient(120deg, #43e97b 0%, #38f9d7 100%); | |
| 191 | + padding: 32rpx 40rpx; | |
| 192 | + color: #fff; | |
| 193 | + font-size: 36rpx; | |
| 194 | + font-weight: 600; | |
| 195 | + letter-spacing: 2rpx; | |
| 196 | + text-align: center; | |
| 197 | + } | |
| 198 | + | |
| 199 | + .detail-content { | |
| 200 | + padding: 40rpx; | |
| 201 | + } | |
| 202 | + | |
| 203 | + .loading { | |
| 204 | + text-align: center; | |
| 205 | + padding: 80rpx 40rpx; | |
| 206 | + color: #6a9c6a; | |
| 207 | + font-size: 28rpx; | |
| 208 | + } | |
| 209 | + | |
| 210 | + .error-state { | |
| 211 | + text-align: center; | |
| 212 | + padding: 80rpx 40rpx; | |
| 213 | + color: #f56c6c; | |
| 214 | + font-size: 28rpx; | |
| 215 | + } | |
| 216 | + | |
| 217 | + .retry-btn { | |
| 218 | + margin-top: 32rpx; | |
| 219 | + } | |
| 220 | + | |
| 221 | + .info-sections { | |
| 222 | + display: flex; | |
| 223 | + flex-direction: column; | |
| 224 | + gap: 32rpx; | |
| 225 | + } | |
| 226 | + | |
| 227 | + .info-section { | |
| 228 | + background: #f9fff9; | |
| 229 | + border-radius: 24rpx; | |
| 230 | + padding: 32rpx; | |
| 231 | + border: 2rpx solid #e8f5e9; | |
| 232 | + } | |
| 233 | + | |
| 234 | + .section-title { | |
| 235 | + display: flex; | |
| 236 | + align-items: center; | |
| 237 | + gap: 12rpx; | |
| 238 | + font-size: 32rpx; | |
| 239 | + font-weight: 600; | |
| 240 | + color: #2e7d32; | |
| 241 | + margin-bottom: 24rpx; | |
| 242 | + padding-bottom: 16rpx; | |
| 243 | + border-bottom: 2rpx solid #c8e6c9; | |
| 244 | + } | |
| 245 | + | |
| 246 | + .section-icon { | |
| 247 | + font-size: 32rpx; | |
| 248 | + } | |
| 249 | + | |
| 250 | + .info-grid { | |
| 251 | + display: grid; | |
| 252 | + grid-template-columns: 1fr 1fr; | |
| 253 | + gap: 24rpx; | |
| 254 | + } | |
| 255 | + | |
| 256 | + .info-item { | |
| 257 | + display: flex; | |
| 258 | + flex-direction: column; | |
| 259 | + gap: 8rpx; | |
| 260 | + } | |
| 261 | + | |
| 262 | + .info-item.full-width { | |
| 263 | + grid-column: 1 / -1; | |
| 264 | + } | |
| 265 | + | |
| 266 | + .info-label { | |
| 267 | + font-size: 24rpx; | |
| 268 | + color: #6a9c6a; | |
| 269 | + font-weight: 500; | |
| 270 | + } | |
| 271 | + | |
| 272 | + .info-value { | |
| 273 | + font-size: 28rpx; | |
| 274 | + color: #2e7d32; | |
| 275 | + font-weight: 500; | |
| 276 | + word-break: break-all; | |
| 277 | + } | |
| 278 | + | |
| 279 | + .info-value.amount { | |
| 280 | + color: #f57c00; | |
| 281 | + font-weight: 600; | |
| 282 | + font-size: 32rpx; | |
| 283 | + } | |
| 284 | + | |
| 285 | + .info-value.amount.total { | |
| 286 | + font-size: 36rpx; | |
| 287 | + color: #e65100; | |
| 288 | + } | |
| 289 | + | |
| 290 | + .flow-type-badge { | |
| 291 | + display: flex; | |
| 292 | + align-items: center; | |
| 293 | + padding: 8rpx 24rpx; | |
| 294 | + border-radius: 40rpx; | |
| 295 | + font-size: 24rpx; | |
| 296 | + font-weight: 500; | |
| 297 | + width: fit-content; | |
| 298 | + } | |
| 299 | + | |
| 300 | + .flow-type-badge.send-type { | |
| 301 | + background: #e3f2fd; | |
| 302 | + color: #1976d2; | |
| 303 | + border: 2rpx solid #90caf9; | |
| 304 | + } | |
| 305 | + | |
| 306 | + .flow-type-badge.return-type { | |
| 307 | + background: #e8f5e9; | |
| 308 | + color: #388e3c; | |
| 309 | + border: 2rpx solid #a5d6a7; | |
| 310 | + } | |
| 311 | + | |
| 312 | + .flow-type-icon { | |
| 313 | + margin-right: 8rpx; | |
| 314 | + font-size: 28rpx; | |
| 315 | + } | |
| 316 | + | |
| 317 | + .flow-type-text { | |
| 318 | + font-size: 24rpx; | |
| 319 | + } | |
| 320 | + | |
| 321 | + .status-badge { | |
| 322 | + display: inline-block; | |
| 323 | + padding: 8rpx 24rpx; | |
| 324 | + border-radius: 40rpx; | |
| 325 | + font-size: 24rpx; | |
| 326 | + font-weight: 500; | |
| 327 | + text-align: center; | |
| 328 | + width: fit-content; | |
| 329 | + } | |
| 330 | + | |
| 331 | + .status-badge.effective { | |
| 332 | + background: #e8f5e9; | |
| 333 | + color: #2e7d32; | |
| 334 | + border: 2rpx solid #c8e6c9; | |
| 335 | + } | |
| 336 | + | |
| 337 | + .status-badge.ineffective { | |
| 338 | + background: #ffebee; | |
| 339 | + color: #c62828; | |
| 340 | + border: 2rpx solid #ffcdd2; | |
| 341 | + } | |
| 342 | +</style> | |
| 343 | + | ... | ... |
绿纤uni-app/pages/laundry-flow-list/laundry-flow-list.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="container"> | |
| 3 | + <!-- 查询条件 --> | |
| 4 | + <view class="filter-card"> | |
| 5 | + <view class="filter-row"> | |
| 6 | + <view class="filter-item"> | |
| 7 | + <text class="filter-label">流水类型</text> | |
| 8 | + <picker mode="selector" :range="flowTypeOptions" range-key="label" :value="flowTypeIndex" @change="onFlowTypeChange"> | |
| 9 | + <view class="picker-view"> | |
| 10 | + {{ flowTypeOptions[flowTypeIndex].label }} | |
| 11 | + <text class="picker-arrow">▼</text> | |
| 12 | + </view> | |
| 13 | + </picker> | |
| 14 | + </view> | |
| 15 | + </view> | |
| 16 | + <view class="filter-buttons"> | |
| 17 | + <view class="filter-btn" @click="search">查询</view> | |
| 18 | + <view class="filter-btn reset-btn" @click="reset">重置</view> | |
| 19 | + </view> | |
| 20 | + </view> | |
| 21 | + | |
| 22 | + <!-- 操作按钮 --> | |
| 23 | + <view class="action-buttons"> | |
| 24 | + <view class="action-btn send-btn" @click="goToSend"> | |
| 25 | + <text class="btn-text">创建送出记录</text> | |
| 26 | + </view> | |
| 27 | + <view class="action-btn return-btn" @click="goToReturn"> | |
| 28 | + <text class="btn-text">创建送回记录</text> | |
| 29 | + </view> | |
| 30 | + </view> | |
| 31 | + | |
| 32 | + <!-- 数据列表 --> | |
| 33 | + <view class="list-card"> | |
| 34 | + <view class="list-header"> | |
| 35 | + <text class="header-text">毛巾记录</text> | |
| 36 | + <text class="total-count">共 {{ totalCount }} 条</text> | |
| 37 | + </view> | |
| 38 | + | |
| 39 | + <scroll-view scroll-y class="list-content" @scrolltolower="loadMore"> | |
| 40 | + <view v-for="(item, index) in dataList" :key="index" class="list-item" @click="viewDetail(item)"> | |
| 41 | + <view class="item-header"> | |
| 42 | + <view class="flow-type-badge" :class="item.flowType === 0 ? 'send-type' : 'return-type'"> | |
| 43 | + <text class="flow-type-icon">{{ item.flowType === 0 ? '📤' : '📥' }}</text> | |
| 44 | + <text class="flow-type-text">{{ item.flowTypeName || '无' }}</text> | |
| 45 | + </view> | |
| 46 | + <view class="create-time">{{ formatTime(item.createTime) }}</view> | |
| 47 | + </view> | |
| 48 | + <view class="item-details"> | |
| 49 | + <view class="detail-item"> | |
| 50 | + <text class="detail-label">批次号:</text> | |
| 51 | + <text class="detail-value">{{ item.batchNumber || '无' }}</text> | |
| 52 | + </view> | |
| 53 | + <view class="detail-item"> | |
| 54 | + <text class="detail-label">门店:</text> | |
| 55 | + <text class="detail-value">{{ item.storeName || '无' }}</text> | |
| 56 | + </view> | |
| 57 | + <view class="detail-item"> | |
| 58 | + <text class="detail-label">产品类型:</text> | |
| 59 | + <text class="detail-value">{{ item.productType || '无' }}</text> | |
| 60 | + </view> | |
| 61 | + <view class="detail-item"> | |
| 62 | + <text class="detail-label">清洗商:</text> | |
| 63 | + <text class="detail-value">{{ item.laundrySupplierName || '无' }}</text> | |
| 64 | + </view> | |
| 65 | + <view class="detail-item"> | |
| 66 | + <text class="detail-label">数量:</text> | |
| 67 | + <text class="detail-value">{{ item.quantity || 0 }}</text> | |
| 68 | + </view> | |
| 69 | + <view class="detail-item"> | |
| 70 | + <text class="detail-label">清洗单价:</text> | |
| 71 | + <text class="detail-value">¥{{ item.laundryPrice || 0 }}</text> | |
| 72 | + </view> | |
| 73 | + <view class="detail-item"> | |
| 74 | + <text class="detail-label">总费用:</text> | |
| 75 | + <text class="detail-value highlight">¥{{ item.totalPrice || 0 }}</text> | |
| 76 | + </view> | |
| 77 | + <view class="detail-item full-width"> | |
| 78 | + <text class="detail-label">备注:</text> | |
| 79 | + <text class="detail-value">{{ item.remark || '无' }}</text> | |
| 80 | + </view> | |
| 81 | + </view> | |
| 82 | + <view class="item-footer"> | |
| 83 | + <view class="status-badge" :class="item.isEffective === 1 ? 'effective' : 'ineffective'"> | |
| 84 | + {{ item.isEffective === 1 ? '有效' : '无效' }} | |
| 85 | + </view> | |
| 86 | + <view class="create-user">创建人: {{ item.createUserName || '无' }}</view> | |
| 87 | + </view> | |
| 88 | + </view> | |
| 89 | + | |
| 90 | + <!-- 空状态 --> | |
| 91 | + <view v-if="!loading && dataList.length === 0" class="empty-state"> | |
| 92 | + <view class="empty-icon">📋</view> | |
| 93 | + <view>暂无记录</view> | |
| 94 | + </view> | |
| 95 | + | |
| 96 | + <!-- 加载状态 --> | |
| 97 | + <view v-if="loading && dataList.length === 0" class="loading">正在加载数据...</view> | |
| 98 | + | |
| 99 | + <!-- 加载更多 --> | |
| 100 | + <u-loadmore v-if="dataList.length > 0" :status="loadmoreStatus" :load-text="loadText"></u-loadmore> | |
| 101 | + </scroll-view> | |
| 102 | + </view> | |
| 103 | + </view> | |
| 104 | +</template> | |
| 105 | + | |
| 106 | +<script> | |
| 107 | + import laundryFlowApi from '@/apis/modules/laundry-flow.js' | |
| 108 | + | |
| 109 | + export default { | |
| 110 | + data() { | |
| 111 | + return { | |
| 112 | + loading: false, | |
| 113 | + dataList: [], | |
| 114 | + currentPage: 1, | |
| 115 | + pageSize: 10, | |
| 116 | + totalCount: 0, | |
| 117 | + hasMore: true, | |
| 118 | + loadmoreStatus: 'loadmore', | |
| 119 | + loadText: { | |
| 120 | + loadmore: '点击或上拉加载更多', | |
| 121 | + loading: '正在加载...', | |
| 122 | + nomore: '没有更多了' | |
| 123 | + }, | |
| 124 | + // 查询条件 | |
| 125 | + query: { | |
| 126 | + flowType: undefined | |
| 127 | + }, | |
| 128 | + flowTypeOptions: [ | |
| 129 | + { label: '全部', value: undefined }, | |
| 130 | + { label: '送出', value: 0 }, | |
| 131 | + { label: '送回', value: 1 } | |
| 132 | + ], | |
| 133 | + flowTypeIndex: 0, | |
| 134 | + userInfo: uni.getStorageSync('userInfo'), | |
| 135 | + newuserInfo: uni.getStorageSync('newuserInfo'), | |
| 136 | + } | |
| 137 | + }, | |
| 138 | + | |
| 139 | + onLoad() { | |
| 140 | + this.initializePage() | |
| 141 | + }, | |
| 142 | + | |
| 143 | + onShow() { | |
| 144 | + // 页面显示时刷新数据(如果从添加页面返回) | |
| 145 | + if (this.dataList.length > 0) { | |
| 146 | + this.refreshData() | |
| 147 | + } | |
| 148 | + }, | |
| 149 | + | |
| 150 | + methods: { | |
| 151 | + // 初始化页面 | |
| 152 | + async initializePage() { | |
| 153 | + try { | |
| 154 | + await this.checkLoginStatus() | |
| 155 | + await this.loadList() | |
| 156 | + } catch (error) { | |
| 157 | + console.error('页面初始化失败:', error) | |
| 158 | + this.showErrorState('页面初始化失败,请刷新重试') | |
| 159 | + } | |
| 160 | + }, | |
| 161 | + | |
| 162 | + // 检查登录状态 | |
| 163 | + async checkLoginStatus() { | |
| 164 | + const token = uni.getStorageSync('token') | |
| 165 | + if (!token) { | |
| 166 | + uni.reLaunch({ | |
| 167 | + url: '/pages/login/login' | |
| 168 | + }) | |
| 169 | + return | |
| 170 | + } | |
| 171 | + }, | |
| 172 | + | |
| 173 | + // 加载列表数据 | |
| 174 | + async loadList() { | |
| 175 | + if (this.loading) return | |
| 176 | + | |
| 177 | + try { | |
| 178 | + this.loading = true | |
| 179 | + | |
| 180 | + const params = { | |
| 181 | + currentPage: this.currentPage, | |
| 182 | + pageSize: this.pageSize | |
| 183 | + } | |
| 184 | + | |
| 185 | + // 添加查询条件 | |
| 186 | + if (this.query.flowType !== undefined) { | |
| 187 | + params.flowType = this.query.flowType | |
| 188 | + } | |
| 189 | + // if (this.userInfo && this.userInfo.userId) { | |
| 190 | + // params.createUser = this.userInfo.userId | |
| 191 | + // } else { | |
| 192 | + // params.createUser = '暂无' | |
| 193 | + // } | |
| 194 | + | |
| 195 | + params.StoreId = this.newuserInfo.mdid || '暂无' | |
| 196 | + const res = await laundryFlowApi.getLaundryFlowList(params) | |
| 197 | + | |
| 198 | + if (res.code === 200) { | |
| 199 | + const data = res.data | |
| 200 | + this.totalCount = data.pagination?.total || 0 | |
| 201 | + const list = data.list || [] | |
| 202 | + | |
| 203 | + if (this.currentPage === 1) { | |
| 204 | + this.dataList = list | |
| 205 | + } else { | |
| 206 | + this.dataList = [...this.dataList, ...list] | |
| 207 | + } | |
| 208 | + | |
| 209 | + this.hasMore = list.length === this.pageSize | |
| 210 | + | |
| 211 | + if (this.hasMore) { | |
| 212 | + this.loadmoreStatus = 'loadmore' | |
| 213 | + } else { | |
| 214 | + this.loadmoreStatus = 'nomore' | |
| 215 | + } | |
| 216 | + } else { | |
| 217 | + throw new Error(res.message || '获取数据失败') | |
| 218 | + } | |
| 219 | + | |
| 220 | + } catch (error) { | |
| 221 | + console.error('加载列表失败:', error) | |
| 222 | + if (this.currentPage === 1) { | |
| 223 | + this.showErrorState('加载数据失败,请重试') | |
| 224 | + } else { | |
| 225 | + this.showMessage('加载更多数据失败', 'error') | |
| 226 | + } | |
| 227 | + } finally { | |
| 228 | + this.loading = false | |
| 229 | + } | |
| 230 | + }, | |
| 231 | + | |
| 232 | + // 刷新数据 | |
| 233 | + refreshData() { | |
| 234 | + this.currentPage = 1 | |
| 235 | + this.hasMore = true | |
| 236 | + this.loadmoreStatus = 'loadmore' | |
| 237 | + this.dataList = [] | |
| 238 | + this.loadList() | |
| 239 | + }, | |
| 240 | + | |
| 241 | + // 加载更多 | |
| 242 | + async loadMore() { | |
| 243 | + if (this.loading || !this.hasMore || this.loadmoreStatus === 'nomore') return | |
| 244 | + | |
| 245 | + this.loadmoreStatus = 'loading' | |
| 246 | + this.currentPage++ | |
| 247 | + await this.loadList() | |
| 248 | + }, | |
| 249 | + | |
| 250 | + // 查询 | |
| 251 | + search() { | |
| 252 | + this.refreshData() | |
| 253 | + }, | |
| 254 | + | |
| 255 | + // 重置 | |
| 256 | + reset() { | |
| 257 | + this.query = { | |
| 258 | + flowType: undefined | |
| 259 | + } | |
| 260 | + this.flowTypeIndex = 0 | |
| 261 | + this.refreshData() | |
| 262 | + }, | |
| 263 | + | |
| 264 | + // 流水类型变化 | |
| 265 | + onFlowTypeChange(e) { | |
| 266 | + this.flowTypeIndex = e.detail.value | |
| 267 | + this.query.flowType = this.flowTypeOptions[this.flowTypeIndex].value | |
| 268 | + }, | |
| 269 | + | |
| 270 | + // 跳转到送出记录页面 | |
| 271 | + goToSend() { | |
| 272 | + uni.navigateTo({ | |
| 273 | + url: '/pages/laundry-flow-send/laundry-flow-send' | |
| 274 | + }) | |
| 275 | + }, | |
| 276 | + | |
| 277 | + // 跳转到送回记录页面 | |
| 278 | + goToReturn() { | |
| 279 | + uni.navigateTo({ | |
| 280 | + url: '/pages/laundry-flow-return/laundry-flow-return' | |
| 281 | + }) | |
| 282 | + }, | |
| 283 | + | |
| 284 | + // 查看详情 | |
| 285 | + viewDetail(item) { | |
| 286 | + uni.navigateTo({ | |
| 287 | + url: `/pages/laundry-flow-detail/laundry-flow-detail?id=${item.id}` | |
| 288 | + }) | |
| 289 | + }, | |
| 290 | + | |
| 291 | + // 格式化时间 | |
| 292 | + formatTime(timestamp) { | |
| 293 | + if (!timestamp) return '无' | |
| 294 | + const date = new Date(timestamp) | |
| 295 | + return date.toLocaleString('zh-CN', { | |
| 296 | + year: 'numeric', | |
| 297 | + month: '2-digit', | |
| 298 | + day: '2-digit', | |
| 299 | + hour: '2-digit', | |
| 300 | + minute: '2-digit' | |
| 301 | + }) | |
| 302 | + }, | |
| 303 | + | |
| 304 | + // 显示错误状态 | |
| 305 | + showErrorState(message) { | |
| 306 | + uni.showToast({ | |
| 307 | + title: message, | |
| 308 | + icon: 'none' | |
| 309 | + }) | |
| 310 | + }, | |
| 311 | + | |
| 312 | + // 显示消息提示 | |
| 313 | + showMessage(message, type = 'info') { | |
| 314 | + uni.showToast({ | |
| 315 | + title: message, | |
| 316 | + icon: type === 'error' ? 'error' : 'none' | |
| 317 | + }) | |
| 318 | + } | |
| 319 | + } | |
| 320 | + } | |
| 321 | +</script> | |
| 322 | + | |
| 323 | +<style lang="scss" scoped> | |
| 324 | + .container { | |
| 325 | + min-height: 100vh; | |
| 326 | + background: linear-gradient(135deg, #e8f5e9 0%, #b2dfdb 100%); | |
| 327 | + padding: 40rpx 40rpx; | |
| 328 | + box-sizing: border-box; | |
| 329 | + } | |
| 330 | + | |
| 331 | + .filter-card { | |
| 332 | + background: #fff; | |
| 333 | + border-radius: 32rpx; | |
| 334 | + box-shadow: 0 8rpx 32rpx rgba(76, 175, 80, 0.1); | |
| 335 | + padding: 40rpx; | |
| 336 | + margin-bottom: 40rpx; | |
| 337 | + } | |
| 338 | + | |
| 339 | + .filter-row { | |
| 340 | + display: flex; | |
| 341 | + flex-wrap: wrap; | |
| 342 | + gap: 24rpx; | |
| 343 | + margin-bottom: 32rpx; | |
| 344 | + } | |
| 345 | + | |
| 346 | + .filter-item { | |
| 347 | + flex: 1; | |
| 348 | + min-width: 200rpx; | |
| 349 | + } | |
| 350 | + | |
| 351 | + .filter-label { | |
| 352 | + font-size: 28rpx; | |
| 353 | + color: #2e7d32; | |
| 354 | + margin-bottom: 16rpx; | |
| 355 | + display: block; | |
| 356 | + } | |
| 357 | + | |
| 358 | + .picker-view { | |
| 359 | + display: flex; | |
| 360 | + align-items: center; | |
| 361 | + justify-content: space-between; | |
| 362 | + background: #f9fff9; | |
| 363 | + border: 3rpx solid #c8e6c9; | |
| 364 | + border-radius: 24rpx; | |
| 365 | + padding: 24rpx 32rpx; | |
| 366 | + font-size: 28rpx; | |
| 367 | + color: #2e7d32; | |
| 368 | + } | |
| 369 | + | |
| 370 | + .picker-arrow { | |
| 371 | + font-size: 24rpx; | |
| 372 | + color: #6a9c6a; | |
| 373 | + margin-left: 16rpx; | |
| 374 | + } | |
| 375 | + | |
| 376 | + .filter-buttons { | |
| 377 | + display: flex; | |
| 378 | + gap: 24rpx; | |
| 379 | + } | |
| 380 | + | |
| 381 | + .filter-btn { | |
| 382 | + flex: 1; | |
| 383 | + text-align: center; | |
| 384 | + background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); | |
| 385 | + color: #fff; | |
| 386 | + padding: 24rpx; | |
| 387 | + border-radius: 24rpx; | |
| 388 | + font-size: 28rpx; | |
| 389 | + font-weight: 600; | |
| 390 | + box-shadow: 0 4rpx 16rpx rgba(67, 233, 123, 0.3); | |
| 391 | + } | |
| 392 | + | |
| 393 | + .filter-btn.reset-btn { | |
| 394 | + background: linear-gradient(135deg, #f5f5f5 0%, #e0e0e0 100%); | |
| 395 | + color: #666; | |
| 396 | + box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1); | |
| 397 | + } | |
| 398 | + | |
| 399 | + .action-buttons { | |
| 400 | + display: flex; | |
| 401 | + gap: 24rpx; | |
| 402 | + margin-bottom: 40rpx; | |
| 403 | + } | |
| 404 | + | |
| 405 | + .action-btn { | |
| 406 | + flex: 1; | |
| 407 | + text-align: center; | |
| 408 | + padding: 28rpx; | |
| 409 | + border-radius: 32rpx; | |
| 410 | + font-size: 28rpx; | |
| 411 | + font-weight: 600; | |
| 412 | + box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.15); | |
| 413 | + } | |
| 414 | + | |
| 415 | + .send-btn { | |
| 416 | + background: linear-gradient(135deg, #409EFF 0%, #66b1ff 100%); | |
| 417 | + color: #fff; | |
| 418 | + } | |
| 419 | + | |
| 420 | + .return-btn { | |
| 421 | + background: linear-gradient(135deg, #67C23A 0%, #85ce61 100%); | |
| 422 | + color: #fff; | |
| 423 | + } | |
| 424 | + | |
| 425 | + .btn-text { | |
| 426 | + letter-spacing: 2rpx; | |
| 427 | + } | |
| 428 | + | |
| 429 | + .list-card { | |
| 430 | + background: #fff; | |
| 431 | + border-radius: 32rpx; | |
| 432 | + box-shadow: 0 8rpx 32rpx rgba(76, 175, 80, 0.1); | |
| 433 | + overflow: hidden; | |
| 434 | + max-height: 60vh; | |
| 435 | + } | |
| 436 | + | |
| 437 | + .list-header { | |
| 438 | + background: linear-gradient(120deg, #43e97b 0%, #38f9d7 100%); | |
| 439 | + padding: 32rpx 40rpx; | |
| 440 | + color: #fff; | |
| 441 | + font-weight: 600; | |
| 442 | + letter-spacing: 2rpx; | |
| 443 | + display: flex; | |
| 444 | + justify-content: space-between; | |
| 445 | + align-items: center; | |
| 446 | + } | |
| 447 | + | |
| 448 | + .header-text { | |
| 449 | + font-size: 32rpx; | |
| 450 | + } | |
| 451 | + | |
| 452 | + .total-count { | |
| 453 | + font-size: 28rpx; | |
| 454 | + opacity: 0.9; | |
| 455 | + } | |
| 456 | + | |
| 457 | + .list-content { | |
| 458 | + max-height: calc(60vh - 120rpx); | |
| 459 | + overflow-y: auto; | |
| 460 | + } | |
| 461 | + | |
| 462 | + .list-item { | |
| 463 | + padding: 32rpx 40rpx; | |
| 464 | + border-bottom: 2rpx solid #f0f0f0; | |
| 465 | + cursor: pointer; | |
| 466 | + transition: all 0.2s ease; | |
| 467 | + } | |
| 468 | + | |
| 469 | + .list-item:active { | |
| 470 | + background: #f8fff8; | |
| 471 | + transform: translateX(8rpx); | |
| 472 | + } | |
| 473 | + | |
| 474 | + .list-item:last-child { | |
| 475 | + border-bottom: none; | |
| 476 | + } | |
| 477 | + | |
| 478 | + .item-header { | |
| 479 | + display: flex; | |
| 480 | + justify-content: space-between; | |
| 481 | + align-items: center; | |
| 482 | + margin-bottom: 16rpx; | |
| 483 | + } | |
| 484 | + | |
| 485 | + .flow-type-badge { | |
| 486 | + display: flex; | |
| 487 | + align-items: center; | |
| 488 | + padding: 8rpx 24rpx; | |
| 489 | + border-radius: 40rpx; | |
| 490 | + font-size: 24rpx; | |
| 491 | + font-weight: 500; | |
| 492 | + } | |
| 493 | + | |
| 494 | + .flow-type-badge.send-type { | |
| 495 | + background: #e3f2fd; | |
| 496 | + color: #1976d2; | |
| 497 | + border: 2rpx solid #90caf9; | |
| 498 | + } | |
| 499 | + | |
| 500 | + .flow-type-badge.return-type { | |
| 501 | + background: #e8f5e9; | |
| 502 | + color: #388e3c; | |
| 503 | + border: 2rpx solid #a5d6a7; | |
| 504 | + } | |
| 505 | + | |
| 506 | + .flow-type-icon { | |
| 507 | + margin-right: 8rpx; | |
| 508 | + font-size: 28rpx; | |
| 509 | + } | |
| 510 | + | |
| 511 | + .flow-type-text { | |
| 512 | + font-size: 24rpx; | |
| 513 | + } | |
| 514 | + | |
| 515 | + .create-time { | |
| 516 | + font-size: 24rpx; | |
| 517 | + color: #6a9c6a; | |
| 518 | + } | |
| 519 | + | |
| 520 | + .item-details { | |
| 521 | + display: grid; | |
| 522 | + grid-template-columns: 1fr 1fr; | |
| 523 | + gap: 16rpx; | |
| 524 | + margin-bottom: 16rpx; | |
| 525 | + } | |
| 526 | + | |
| 527 | + .detail-item { | |
| 528 | + display: flex; | |
| 529 | + align-items: center; | |
| 530 | + font-size: 28rpx; | |
| 531 | + color: #555; | |
| 532 | + } | |
| 533 | + | |
| 534 | + .detail-item.full-width { | |
| 535 | + grid-column: 1 / -1; | |
| 536 | + } | |
| 537 | + | |
| 538 | + .detail-label { | |
| 539 | + color: #6a9c6a; | |
| 540 | + margin-right: 12rpx; | |
| 541 | + font-size: 24rpx; | |
| 542 | + } | |
| 543 | + | |
| 544 | + .detail-value { | |
| 545 | + color: #2e7d32; | |
| 546 | + font-weight: 500; | |
| 547 | + } | |
| 548 | + | |
| 549 | + .detail-value.highlight { | |
| 550 | + color: #f57c00; | |
| 551 | + font-weight: 600; | |
| 552 | + } | |
| 553 | + | |
| 554 | + .item-footer { | |
| 555 | + display: flex; | |
| 556 | + justify-content: space-between; | |
| 557 | + align-items: center; | |
| 558 | + margin-top: 16rpx; | |
| 559 | + } | |
| 560 | + | |
| 561 | + .status-badge { | |
| 562 | + display: inline-block; | |
| 563 | + padding: 8rpx 24rpx; | |
| 564 | + border-radius: 40rpx; | |
| 565 | + font-size: 24rpx; | |
| 566 | + font-weight: 500; | |
| 567 | + } | |
| 568 | + | |
| 569 | + .status-badge.effective { | |
| 570 | + background: #e8f5e9; | |
| 571 | + color: #2e7d32; | |
| 572 | + border: 2rpx solid #c8e6c9; | |
| 573 | + } | |
| 574 | + | |
| 575 | + .status-badge.ineffective { | |
| 576 | + background: #ffebee; | |
| 577 | + color: #c62828; | |
| 578 | + border: 2rpx solid #ffcdd2; | |
| 579 | + } | |
| 580 | + | |
| 581 | + .create-user { | |
| 582 | + font-size: 24rpx; | |
| 583 | + color: #6a9c6a; | |
| 584 | + } | |
| 585 | + | |
| 586 | + .loading { | |
| 587 | + text-align: center; | |
| 588 | + padding: 80rpx 40rpx; | |
| 589 | + color: #6a9c6a; | |
| 590 | + font-size: 28rpx; | |
| 591 | + } | |
| 592 | + | |
| 593 | + .empty-state { | |
| 594 | + text-align: center; | |
| 595 | + padding: 120rpx 40rpx; | |
| 596 | + color: #6a9c6a; | |
| 597 | + } | |
| 598 | + | |
| 599 | + .empty-icon { | |
| 600 | + font-size: 120rpx; | |
| 601 | + margin-bottom: 32rpx; | |
| 602 | + opacity: 0.5; | |
| 603 | + } | |
| 604 | +</style> | |
| 605 | + | ... | ... |
绿纤uni-app/pages/laundry-flow-return/laundry-flow-return.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="form-container"> | |
| 3 | + <view class="form-card"> | |
| 4 | + <view class="form-content"> | |
| 5 | + <!-- 批次号 --> | |
| 6 | + <view class="form-group"> | |
| 7 | + <text class="form-label">批次号</text> | |
| 8 | + <view class="input-wrapper"> | |
| 9 | + <view class="custom-select" @tap="openSelectModal('batch')"> | |
| 10 | + <text class="select-text">{{ formData.batchNumber || '请选择批次号' }}</text> | |
| 11 | + <text class="select-arrow">▼</text> | |
| 12 | + </view> | |
| 13 | + </view> | |
| 14 | + </view> | |
| 15 | + | |
| 16 | + <!-- 门店(只读) --> | |
| 17 | + <view class="form-group"> | |
| 18 | + <text class="form-label">门店</text> | |
| 19 | + <view class="input-wrapper"> | |
| 20 | + <u-input v-model="sendRecordInfo.storeName" placeholder="无" | |
| 21 | + disabled class="select-input" /> | |
| 22 | + </view> | |
| 23 | + </view> | |
| 24 | + | |
| 25 | + <!-- 产品类型(只读) --> | |
| 26 | + <view class="form-group"> | |
| 27 | + <text class="form-label">产品类型</text> | |
| 28 | + <view class="input-wrapper"> | |
| 29 | + <u-input v-model="sendRecordInfo.productType" placeholder="无" | |
| 30 | + disabled class="select-input" /> | |
| 31 | + </view> | |
| 32 | + </view> | |
| 33 | + | |
| 34 | + <!-- 送出数量(只读) --> | |
| 35 | + <view class="form-group"> | |
| 36 | + <text class="form-label">送出数量</text> | |
| 37 | + <view class="input-wrapper"> | |
| 38 | + <u-input v-model="sendRecordInfo.quantity" placeholder="无" | |
| 39 | + disabled class="select-input" /> | |
| 40 | + </view> | |
| 41 | + </view> | |
| 42 | + | |
| 43 | + <!-- 清洗商 --> | |
| 44 | + <view class="form-group"> | |
| 45 | + <text class="form-label">清洗商</text> | |
| 46 | + <view class="input-wrapper"> | |
| 47 | + <view class="custom-select" @tap="openSelectModal('supplier')"> | |
| 48 | + <text class="select-text">{{ formData.supplierName || '请选择清洗商' }}</text> | |
| 49 | + <text class="select-arrow">▼</text> | |
| 50 | + </view> | |
| 51 | + </view> | |
| 52 | + </view> | |
| 53 | + | |
| 54 | + <!-- 清洗单价 --> | |
| 55 | + <view class="form-group"> | |
| 56 | + <text class="form-label">清洗单价</text> | |
| 57 | + <view class="input-wrapper"> | |
| 58 | + <u-input v-model="formData.laundryPrice" placeholder="自动填充" | |
| 59 | + disabled class="select-input" /> | |
| 60 | + </view> | |
| 61 | + </view> | |
| 62 | + | |
| 63 | + <!-- 送回数量 --> | |
| 64 | + <view class="form-group"> | |
| 65 | + <text class="form-label">送回数量</text> | |
| 66 | + <view class="input-wrapper"> | |
| 67 | + <u-input v-model="formData.quantity" placeholder="请输入送回数量" | |
| 68 | + type="number" class="select-input" /> | |
| 69 | + </view> | |
| 70 | + </view> | |
| 71 | + | |
| 72 | + <!-- 预计总费用 --> | |
| 73 | + <view class="form-group"> | |
| 74 | + <text class="form-label">预计总费用</text> | |
| 75 | + <view class="input-wrapper"> | |
| 76 | + <u-input v-model="totalPrice" placeholder="自动计算" | |
| 77 | + disabled class="select-input" /> | |
| 78 | + </view> | |
| 79 | + </view> | |
| 80 | + | |
| 81 | + <!-- 备注 --> | |
| 82 | + <view class="form-group"> | |
| 83 | + <text class="form-label">备注</text> | |
| 84 | + <view class="input-wrapper"> | |
| 85 | + <u-input v-model="formData.remark" placeholder="请输入备注(可说明差异原因,如损坏、丢失等)" | |
| 86 | + type="textarea" :maxlength="500" class="select-input" /> | |
| 87 | + </view> | |
| 88 | + </view> | |
| 89 | + | |
| 90 | + <!-- 提交按钮 --> | |
| 91 | + <view class="btn-group"> | |
| 92 | + <button type="submit" class="btn btn-primary" | |
| 93 | + :style="{opacity: isSubmitting ? 0.5 : 1}" | |
| 94 | + @tap="isSubmitting ? null : handleFormSubmit()"> | |
| 95 | + {{ isSubmitting ? '提交中...' : '提交' }} | |
| 96 | + </button> | |
| 97 | + </view> | |
| 98 | + </view> | |
| 99 | + </view> | |
| 100 | + | |
| 101 | + <!-- 选择弹窗 --> | |
| 102 | + <SearchSelectModal | |
| 103 | + :show="showModal" | |
| 104 | + :title="modalTitle" | |
| 105 | + :options="currentOptions" | |
| 106 | + :loading="modalLoading" | |
| 107 | + :has-more="hasMoreData" | |
| 108 | + :search-param="searchParam" | |
| 109 | + @confirm="handleModalConfirm" | |
| 110 | + @close="closeModal" | |
| 111 | + @load-more="handleLoadMore" | |
| 112 | + @refresh="handleRefresh" | |
| 113 | + @search="handleSearch" /> | |
| 114 | + </view> | |
| 115 | +</template> | |
| 116 | + | |
| 117 | +<script> | |
| 118 | + import SearchSelectModal from '@/components/SearchSelectModal.vue' | |
| 119 | + import laundryFlowApi from '@/apis/modules/laundry-flow.js' | |
| 120 | + | |
| 121 | + export default { | |
| 122 | + components: { | |
| 123 | + SearchSelectModal | |
| 124 | + }, | |
| 125 | + data() { | |
| 126 | + return { | |
| 127 | + isSubmitting: false, | |
| 128 | + formData: { | |
| 129 | + batchNumber: '', | |
| 130 | + laundrySupplierId: '', | |
| 131 | + supplierName: '', | |
| 132 | + laundryPrice: '', | |
| 133 | + quantity: 0, | |
| 134 | + remark: '' | |
| 135 | + }, | |
| 136 | + sendRecordInfo: { | |
| 137 | + storeName: '', | |
| 138 | + productType: '', | |
| 139 | + quantity: 0 | |
| 140 | + }, | |
| 141 | + selectedSupplier: null, | |
| 142 | + batchList: [], | |
| 143 | + allSupplierList: [], | |
| 144 | + // 选择弹窗相关 | |
| 145 | + showModal: false, | |
| 146 | + modalTitle: '', | |
| 147 | + currentSelectField: '', | |
| 148 | + currentOptions: [], | |
| 149 | + modalLoading: false, | |
| 150 | + hasMoreData: true, | |
| 151 | + currentPage: 1, | |
| 152 | + pageSize: 20, | |
| 153 | + searchKeyword: '', | |
| 154 | + searchParam: '' | |
| 155 | + } | |
| 156 | + }, | |
| 157 | + | |
| 158 | + computed: { | |
| 159 | + // 计算总费用 | |
| 160 | + totalPrice() { | |
| 161 | + const quantity = parseFloat(this.formData.quantity) || 0 | |
| 162 | + const price = parseFloat(this.formData.laundryPrice) || 0 | |
| 163 | + return (quantity * price).toFixed(2) | |
| 164 | + }, | |
| 165 | + // 过滤后的清洗商列表(根据产品类型) | |
| 166 | + filteredSupplierList() { | |
| 167 | + if (!this.sendRecordInfo.productType) { | |
| 168 | + return this.allSupplierList | |
| 169 | + } | |
| 170 | + return this.allSupplierList.filter(supplier => supplier.productType === this.sendRecordInfo.productType) | |
| 171 | + } | |
| 172 | + }, | |
| 173 | + | |
| 174 | + onLoad() { | |
| 175 | + this.checkLoginStatus() | |
| 176 | + this.initBatchList() | |
| 177 | + this.initSupplierList() | |
| 178 | + }, | |
| 179 | + | |
| 180 | + methods: { | |
| 181 | + // 检查登录状态 | |
| 182 | + checkLoginStatus() { | |
| 183 | + const token = uni.getStorageSync('token') | |
| 184 | + if (!token) { | |
| 185 | + uni.reLaunch({ | |
| 186 | + url: '/pages/login/login' | |
| 187 | + }) | |
| 188 | + return | |
| 189 | + } | |
| 190 | + }, | |
| 191 | + | |
| 192 | + // 初始化批次列表 | |
| 193 | + async initBatchList() { | |
| 194 | + try { | |
| 195 | + // 获取所有送出记录 | |
| 196 | + const sendRes = await laundryFlowApi.getLaundryFlowList({ | |
| 197 | + currentPage: 1, | |
| 198 | + pageSize: 1000, | |
| 199 | + flowType: 0, | |
| 200 | + isEffective: 1 | |
| 201 | + }) | |
| 202 | + | |
| 203 | + if (sendRes.code === 200 && sendRes.data && sendRes.data.list) { | |
| 204 | + const sendRecords = sendRes.data.list | |
| 205 | + | |
| 206 | + // 获取所有送回记录 | |
| 207 | + const returnRes = await laundryFlowApi.getLaundryFlowList({ | |
| 208 | + currentPage: 1, | |
| 209 | + pageSize: 1000, | |
| 210 | + flowType: 1, | |
| 211 | + isEffective: 1 | |
| 212 | + }) | |
| 213 | + | |
| 214 | + let returnRecords = [] | |
| 215 | + if (returnRes.code === 200 && returnRes.data && returnRes.data.list) { | |
| 216 | + returnRecords = returnRes.data.list | |
| 217 | + } | |
| 218 | + | |
| 219 | + // 按批次号分组统计送回数量 | |
| 220 | + const returnMap = {} | |
| 221 | + returnRecords.forEach(item => { | |
| 222 | + if (!returnMap[item.batchNumber]) { | |
| 223 | + returnMap[item.batchNumber] = 0 | |
| 224 | + } | |
| 225 | + returnMap[item.batchNumber] += item.quantity | |
| 226 | + }) | |
| 227 | + | |
| 228 | + // 过滤出可以送回记录的批次(送出数量 > 已送回数量) | |
| 229 | + this.batchList = sendRecords.filter(send => { | |
| 230 | + const returnedQty = returnMap[send.batchNumber] || 0 | |
| 231 | + return send.quantity > returnedQty | |
| 232 | + }).map(send => ({ | |
| 233 | + batchNumber: send.batchNumber, | |
| 234 | + storeName: send.storeName, | |
| 235 | + productType: send.productType, | |
| 236 | + quantity: send.quantity, | |
| 237 | + returnedQty: returnMap[send.batchNumber] || 0 | |
| 238 | + })) | |
| 239 | + } else { | |
| 240 | + this.batchList = [] | |
| 241 | + } | |
| 242 | + } catch (error) { | |
| 243 | + console.error('初始化批次列表失败:', error) | |
| 244 | + this.batchList = [] | |
| 245 | + } | |
| 246 | + }, | |
| 247 | + | |
| 248 | + // 初始化清洗商列表 | |
| 249 | + async initSupplierList() { | |
| 250 | + try { | |
| 251 | + const res = await laundryFlowApi.getSupplierList({ | |
| 252 | + currentPage: 1, | |
| 253 | + pageSize: 1000, | |
| 254 | + isEffective: 1 | |
| 255 | + }) | |
| 256 | + | |
| 257 | + if (res.code === 200 && res.data && res.data.list) { | |
| 258 | + this.allSupplierList = res.data.list | |
| 259 | + } else { | |
| 260 | + this.allSupplierList = [] | |
| 261 | + } | |
| 262 | + } catch (error) { | |
| 263 | + console.error('初始化清洗商列表失败:', error) | |
| 264 | + this.allSupplierList = [] | |
| 265 | + } | |
| 266 | + }, | |
| 267 | + | |
| 268 | + // 打开选择弹窗 | |
| 269 | + openSelectModal(field) { | |
| 270 | + this.currentSelectField = field | |
| 271 | + this.currentPage = 1 | |
| 272 | + this.hasMoreData = true | |
| 273 | + this.searchKeyword = '' | |
| 274 | + this.searchParam = '' | |
| 275 | + | |
| 276 | + if (field === 'batch') { | |
| 277 | + this.modalTitle = '选择批次号' | |
| 278 | + this.searchParam = '请输入批次号' | |
| 279 | + } else if (field === 'supplier') { | |
| 280 | + this.modalTitle = '选择清洗商' | |
| 281 | + this.searchParam = '请输入清洗商名称' | |
| 282 | + } | |
| 283 | + | |
| 284 | + this.showModal = true | |
| 285 | + this.loadOptionsData(field, 1) | |
| 286 | + }, | |
| 287 | + | |
| 288 | + // 加载选项数据 | |
| 289 | + async loadOptionsData(fieldId, page = 1, searchKeyword = '') { | |
| 290 | + this.modalLoading = true | |
| 291 | + let options = [] | |
| 292 | + | |
| 293 | + try { | |
| 294 | + if (fieldId === 'batch') { | |
| 295 | + options = this.getBatchOptions(searchKeyword) | |
| 296 | + } else if (fieldId === 'supplier') { | |
| 297 | + options = this.getSupplierOptions(searchKeyword) | |
| 298 | + } | |
| 299 | + | |
| 300 | + if (page === 1) { | |
| 301 | + this.currentOptions = options | |
| 302 | + } else { | |
| 303 | + this.currentOptions = [...this.currentOptions, ...options] | |
| 304 | + } | |
| 305 | + | |
| 306 | + this.hasMoreData = false // 批次和清洗商列表不需要分页 | |
| 307 | + } catch (error) { | |
| 308 | + console.error('加载选项数据失败:', error) | |
| 309 | + this.currentOptions = [] | |
| 310 | + } finally { | |
| 311 | + this.modalLoading = false | |
| 312 | + } | |
| 313 | + }, | |
| 314 | + | |
| 315 | + // 获取批次选项 | |
| 316 | + getBatchOptions(searchKeyword = '') { | |
| 317 | + let list = this.batchList | |
| 318 | + | |
| 319 | + if (searchKeyword) { | |
| 320 | + list = list.filter(item => | |
| 321 | + item.batchNumber.includes(searchKeyword) || | |
| 322 | + item.storeName.includes(searchKeyword) || | |
| 323 | + item.productType.includes(searchKeyword) | |
| 324 | + ) | |
| 325 | + } | |
| 326 | + | |
| 327 | + return list.map(item => ({ | |
| 328 | + value: item.batchNumber, | |
| 329 | + label: `${item.batchNumber} (${item.storeName} - ${item.productType} - 送出${item.quantity}件)`, | |
| 330 | + batchNumber: item.batchNumber, | |
| 331 | + storeName: item.storeName, | |
| 332 | + productType: item.productType, | |
| 333 | + quantity: item.quantity | |
| 334 | + })) | |
| 335 | + }, | |
| 336 | + | |
| 337 | + // 获取清洗商选项 | |
| 338 | + getSupplierOptions(searchKeyword = '') { | |
| 339 | + let list = this.filteredSupplierList | |
| 340 | + | |
| 341 | + if (searchKeyword) { | |
| 342 | + list = list.filter(item => | |
| 343 | + item.supplierName && item.supplierName.includes(searchKeyword) | |
| 344 | + ) | |
| 345 | + } | |
| 346 | + | |
| 347 | + return list.map(item => ({ | |
| 348 | + value: item.id, | |
| 349 | + label: item.supplierName || '未知清洗商', | |
| 350 | + supplierName: item.supplierName, | |
| 351 | + productType: item.productType, | |
| 352 | + laundryPrice: item.laundryPrice, | |
| 353 | + id: item.id | |
| 354 | + })) | |
| 355 | + }, | |
| 356 | + | |
| 357 | + // 处理弹窗确认 | |
| 358 | + handleModalConfirm(item) { | |
| 359 | + if (this.currentSelectField === 'batch') { | |
| 360 | + this.formData.batchNumber = item.batchNumber | |
| 361 | + this.sendRecordInfo = { | |
| 362 | + storeName: item.storeName || '', | |
| 363 | + productType: item.productType || '', | |
| 364 | + quantity: item.quantity || 0 | |
| 365 | + } | |
| 366 | + // 清空已选择的清洗商 | |
| 367 | + this.formData.laundrySupplierId = '' | |
| 368 | + this.formData.supplierName = '' | |
| 369 | + this.formData.laundryPrice = '' | |
| 370 | + this.selectedSupplier = null | |
| 371 | + } else if (this.currentSelectField === 'supplier') { | |
| 372 | + this.formData.laundrySupplierId = item.value | |
| 373 | + this.formData.supplierName = item.label | |
| 374 | + this.selectedSupplier = item | |
| 375 | + // 自动填充清洗单价 | |
| 376 | + this.formData.laundryPrice = item.laundryPrice || '' | |
| 377 | + // 验证产品类型是否匹配 | |
| 378 | + if (this.sendRecordInfo.productType && item.productType && this.sendRecordInfo.productType !== item.productType) { | |
| 379 | + uni.showToast({ | |
| 380 | + title: `清洗商【${item.supplierName}】不支持清洗产品类型【${this.sendRecordInfo.productType}】`, | |
| 381 | + icon: 'none', | |
| 382 | + duration: 3000 | |
| 383 | + }) | |
| 384 | + } | |
| 385 | + } | |
| 386 | + this.closeModal() | |
| 387 | + }, | |
| 388 | + | |
| 389 | + // 关闭弹窗 | |
| 390 | + closeModal() { | |
| 391 | + this.showModal = false | |
| 392 | + this.currentSelectField = '' | |
| 393 | + this.currentOptions = [] | |
| 394 | + this.modalLoading = false | |
| 395 | + this.hasMoreData = true | |
| 396 | + this.currentPage = 1 | |
| 397 | + this.searchKeyword = '' | |
| 398 | + }, | |
| 399 | + | |
| 400 | + // 处理加载更多 | |
| 401 | + async handleLoadMore() { | |
| 402 | + // 批次和清洗商列表不需要分页 | |
| 403 | + }, | |
| 404 | + | |
| 405 | + // 处理刷新 | |
| 406 | + async handleRefresh() { | |
| 407 | + this.currentPage = 1 | |
| 408 | + this.hasMoreData = true | |
| 409 | + await this.loadOptionsData(this.currentSelectField, 1, this.searchKeyword) | |
| 410 | + }, | |
| 411 | + | |
| 412 | + // 处理搜索 | |
| 413 | + async handleSearch(keyword) { | |
| 414 | + this.searchKeyword = keyword | |
| 415 | + this.currentPage = 1 | |
| 416 | + this.hasMoreData = true | |
| 417 | + await this.loadOptionsData(this.currentSelectField, 1, keyword) | |
| 418 | + }, | |
| 419 | + | |
| 420 | + // 表单提交 | |
| 421 | + async handleFormSubmit() { | |
| 422 | + // 验证必填字段 | |
| 423 | + if (!this.formData.batchNumber) { | |
| 424 | + uni.showToast({ | |
| 425 | + title: '请选择批次号', | |
| 426 | + icon: 'none' | |
| 427 | + }) | |
| 428 | + return | |
| 429 | + } | |
| 430 | + | |
| 431 | + if (!this.formData.laundrySupplierId) { | |
| 432 | + uni.showToast({ | |
| 433 | + title: '请选择清洗商', | |
| 434 | + icon: 'none' | |
| 435 | + }) | |
| 436 | + return | |
| 437 | + } | |
| 438 | + | |
| 439 | + // 验证产品类型是否匹配 | |
| 440 | + if (this.selectedSupplier && this.sendRecordInfo.productType && this.sendRecordInfo.productType !== this.selectedSupplier.productType) { | |
| 441 | + uni.showToast({ | |
| 442 | + title: `清洗商【${this.selectedSupplier.supplierName}】不支持清洗产品类型【${this.sendRecordInfo.productType}】`, | |
| 443 | + icon: 'none', | |
| 444 | + duration: 3000 | |
| 445 | + }) | |
| 446 | + return | |
| 447 | + } | |
| 448 | + | |
| 449 | + if (!this.formData.quantity || this.formData.quantity <= 0) { | |
| 450 | + uni.showToast({ | |
| 451 | + title: '请输入有效的送回数量', | |
| 452 | + icon: 'none' | |
| 453 | + }) | |
| 454 | + return | |
| 455 | + } | |
| 456 | + | |
| 457 | + this.isSubmitting = true | |
| 458 | + | |
| 459 | + try { | |
| 460 | + uni.showLoading({ | |
| 461 | + title: '提交中...' | |
| 462 | + }) | |
| 463 | + | |
| 464 | + const submitData = { | |
| 465 | + batchNumber: this.formData.batchNumber, | |
| 466 | + laundrySupplierId: this.formData.laundrySupplierId, | |
| 467 | + quantity: parseInt(this.formData.quantity), | |
| 468 | + remark: this.formData.remark || '' | |
| 469 | + } | |
| 470 | + | |
| 471 | + const res = await laundryFlowApi.createReturnRecord(submitData) | |
| 472 | + | |
| 473 | + if (res.code === 200) { | |
| 474 | + uni.showToast({ | |
| 475 | + title: res.msg || '创建成功', | |
| 476 | + icon: 'success' | |
| 477 | + }) | |
| 478 | + setTimeout(() => { | |
| 479 | + uni.navigateBack() | |
| 480 | + }, 1500) | |
| 481 | + } else { | |
| 482 | + throw new Error(res.msg || res.message || '创建失败') | |
| 483 | + } | |
| 484 | + } catch (error) { | |
| 485 | + console.error('提交失败:', error) | |
| 486 | + uni.showToast({ | |
| 487 | + title: error.message || error.msg || '创建失败,请重试', | |
| 488 | + icon: 'none', | |
| 489 | + duration: 3000 | |
| 490 | + }) | |
| 491 | + } finally { | |
| 492 | + this.isSubmitting = false | |
| 493 | + uni.hideLoading() | |
| 494 | + } | |
| 495 | + } | |
| 496 | + } | |
| 497 | + } | |
| 498 | +</script> | |
| 499 | + | |
| 500 | +<style lang="scss" scoped> | |
| 501 | + .form-container { | |
| 502 | + min-height: 100vh; | |
| 503 | + background: linear-gradient(135deg, #e8f5e9 0%, #b2dfdb 100%); | |
| 504 | + padding: 40rpx; | |
| 505 | + box-sizing: border-box; | |
| 506 | + } | |
| 507 | + | |
| 508 | + .form-card { | |
| 509 | + background: #fff; | |
| 510 | + border-radius: 32rpx; | |
| 511 | + box-shadow: 0 8rpx 32rpx rgba(76, 175, 80, 0.1); | |
| 512 | + overflow: hidden; | |
| 513 | + } | |
| 514 | + | |
| 515 | + .form-content { | |
| 516 | + padding: 40rpx; | |
| 517 | + } | |
| 518 | + | |
| 519 | + .form-group { | |
| 520 | + margin-bottom: 40rpx; | |
| 521 | + } | |
| 522 | + | |
| 523 | + .form-label { | |
| 524 | + display: block; | |
| 525 | + font-size: 28rpx; | |
| 526 | + color: #2e7d32; | |
| 527 | + margin-bottom: 16rpx; | |
| 528 | + font-weight: 500; | |
| 529 | + } | |
| 530 | + | |
| 531 | + .input-wrapper { | |
| 532 | + width: 100%; | |
| 533 | + } | |
| 534 | + | |
| 535 | + .custom-select { | |
| 536 | + display: flex; | |
| 537 | + align-items: center; | |
| 538 | + justify-content: space-between; | |
| 539 | + background: #f9fff9; | |
| 540 | + border: 3rpx solid #c8e6c9; | |
| 541 | + border-radius: 24rpx; | |
| 542 | + padding: 24rpx 32rpx; | |
| 543 | + min-height: 88rpx; | |
| 544 | + box-sizing: border-box; | |
| 545 | + } | |
| 546 | + | |
| 547 | + .select-text { | |
| 548 | + flex: 1; | |
| 549 | + font-size: 28rpx; | |
| 550 | + color: #2e7d32; | |
| 551 | + } | |
| 552 | + | |
| 553 | + .select-text:empty::before { | |
| 554 | + content: '请选择'; | |
| 555 | + color: #999; | |
| 556 | + } | |
| 557 | + | |
| 558 | + .select-arrow { | |
| 559 | + font-size: 24rpx; | |
| 560 | + color: #6a9c6a; | |
| 561 | + margin-left: 16rpx; | |
| 562 | + } | |
| 563 | + | |
| 564 | + .select-input { | |
| 565 | + background: #f9fff9; | |
| 566 | + border: 3rpx solid #c8e6c9; | |
| 567 | + border-radius: 24rpx; | |
| 568 | + padding: 24rpx 32rpx; | |
| 569 | + font-size: 28rpx; | |
| 570 | + color: #2e7d32; | |
| 571 | + min-height: 88rpx; | |
| 572 | + box-sizing: border-box; | |
| 573 | + } | |
| 574 | + | |
| 575 | + .btn-group { | |
| 576 | + margin-top: 60rpx; | |
| 577 | + } | |
| 578 | + | |
| 579 | + .btn { | |
| 580 | + width: 100%; | |
| 581 | + height: 96rpx; | |
| 582 | + border-radius: 32rpx; | |
| 583 | + font-size: 32rpx; | |
| 584 | + font-weight: 600; | |
| 585 | + border: none; | |
| 586 | + display: flex; | |
| 587 | + align-items: center; | |
| 588 | + justify-content: center; | |
| 589 | + letter-spacing: 2rpx; | |
| 590 | + } | |
| 591 | + | |
| 592 | + .btn-primary { | |
| 593 | + background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); | |
| 594 | + color: #fff; | |
| 595 | + box-shadow: 0 8rpx 24rpx rgba(67, 233, 123, 0.3); | |
| 596 | + } | |
| 597 | + | |
| 598 | + .btn-primary:active { | |
| 599 | + transform: scale(0.98); | |
| 600 | + box-shadow: 0 4rpx 12rpx rgba(67, 233, 123, 0.4); | |
| 601 | + } | |
| 602 | +</style> | |
| 603 | + | ... | ... |
绿纤uni-app/pages/laundry-flow-send/laundry-flow-send.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="form-container"> | |
| 3 | + <view class="form-card"> | |
| 4 | + <view class="form-content"> | |
| 5 | + <!-- 门店 --> | |
| 6 | + <view class="form-group"> | |
| 7 | + <text class="form-label">门店</text> | |
| 8 | + <view class="input-wrapper"> | |
| 9 | + <view class="custom-select" @tap="openSelectModal('store')"> | |
| 10 | + <text class="select-text">{{ formData.storeName || '请选择门店' }}</text> | |
| 11 | + <text class="select-arrow">▼</text> | |
| 12 | + </view> | |
| 13 | + </view> | |
| 14 | + </view> | |
| 15 | + | |
| 16 | + <!-- 产品类型 --> | |
| 17 | + <view class="form-group"> | |
| 18 | + <text class="form-label">产品类型</text> | |
| 19 | + <view class="input-wrapper"> | |
| 20 | + <view class="custom-select" @tap="openSelectModal('productType')"> | |
| 21 | + <text class="select-text">{{ formData.productType || '请选择产品类型' }}</text> | |
| 22 | + <text class="select-arrow">▼</text> | |
| 23 | + </view> | |
| 24 | + </view> | |
| 25 | + </view> | |
| 26 | + | |
| 27 | + <!-- 清洗商 --> | |
| 28 | + <view class="form-group"> | |
| 29 | + <text class="form-label">清洗商</text> | |
| 30 | + <view class="input-wrapper"> | |
| 31 | + <view class="custom-select" @tap="openSelectModal('supplier')"> | |
| 32 | + <text class="select-text">{{ formData.supplierName || '请选择清洗商' }}</text> | |
| 33 | + <text class="select-arrow">▼</text> | |
| 34 | + </view> | |
| 35 | + </view> | |
| 36 | + </view> | |
| 37 | + | |
| 38 | + <!-- 清洗单价 --> | |
| 39 | + <view class="form-group"> | |
| 40 | + <text class="form-label">清洗单价</text> | |
| 41 | + <view class="input-wrapper"> | |
| 42 | + <u-input v-model="formData.laundryPrice" placeholder="自动填充" | |
| 43 | + disabled class="select-input" /> | |
| 44 | + </view> | |
| 45 | + </view> | |
| 46 | + | |
| 47 | + <!-- 送出数量 --> | |
| 48 | + <view class="form-group"> | |
| 49 | + <text class="form-label">送出数量</text> | |
| 50 | + <view class="input-wrapper"> | |
| 51 | + <u-input v-model="formData.quantity" placeholder="请输入送出数量" | |
| 52 | + type="number" class="select-input" /> | |
| 53 | + </view> | |
| 54 | + </view> | |
| 55 | + | |
| 56 | + <!-- 备注 --> | |
| 57 | + <view class="form-group"> | |
| 58 | + <text class="form-label">备注</text> | |
| 59 | + <view class="input-wrapper"> | |
| 60 | + <u-input v-model="formData.remark" placeholder="请输入备注" | |
| 61 | + type="textarea" :maxlength="500" class="select-input" /> | |
| 62 | + </view> | |
| 63 | + </view> | |
| 64 | + | |
| 65 | + <!-- 提交按钮 --> | |
| 66 | + <view class="btn-group"> | |
| 67 | + <button type="submit" class="btn btn-primary" | |
| 68 | + :style="{opacity: isSubmitting ? 0.5 : 1}" | |
| 69 | + @tap="isSubmitting ? null : handleFormSubmit()"> | |
| 70 | + {{ isSubmitting ? '提交中...' : '提交' }} | |
| 71 | + </button> | |
| 72 | + </view> | |
| 73 | + </view> | |
| 74 | + </view> | |
| 75 | + | |
| 76 | + <!-- 选择弹窗 --> | |
| 77 | + <SearchSelectModal | |
| 78 | + :show="showModal" | |
| 79 | + :title="modalTitle" | |
| 80 | + :options="currentOptions" | |
| 81 | + :loading="modalLoading" | |
| 82 | + :has-more="hasMoreData" | |
| 83 | + :search-param="searchParam" | |
| 84 | + @confirm="handleModalConfirm" | |
| 85 | + @close="closeModal" | |
| 86 | + @load-more="handleLoadMore" | |
| 87 | + @refresh="handleRefresh" | |
| 88 | + @search="handleSearch" /> | |
| 89 | + </view> | |
| 90 | +</template> | |
| 91 | + | |
| 92 | +<script> | |
| 93 | + import SearchSelectModal from '@/components/SearchSelectModal.vue' | |
| 94 | + import laundryFlowApi from '@/apis/modules/laundry-flow.js' | |
| 95 | + import memberApi from '@/apis/modules/member.js' | |
| 96 | + | |
| 97 | + export default { | |
| 98 | + components: { | |
| 99 | + SearchSelectModal | |
| 100 | + }, | |
| 101 | + data() { | |
| 102 | + return { | |
| 103 | + isSubmitting: false, | |
| 104 | + formData: { | |
| 105 | + storeId: '', | |
| 106 | + storeName: '', | |
| 107 | + productType: '', | |
| 108 | + laundrySupplierId: '', | |
| 109 | + supplierName: '', | |
| 110 | + laundryPrice: '', | |
| 111 | + quantity: 1, | |
| 112 | + remark: '' | |
| 113 | + }, | |
| 114 | + selectedSupplier: null, | |
| 115 | + productTypeOptions: [], | |
| 116 | + // 选择弹窗相关 | |
| 117 | + showModal: false, | |
| 118 | + modalTitle: '', | |
| 119 | + currentSelectField: '', | |
| 120 | + currentOptions: [], | |
| 121 | + modalLoading: false, | |
| 122 | + hasMoreData: true, | |
| 123 | + currentPage: 1, | |
| 124 | + pageSize: 20, | |
| 125 | + searchKeyword: '', | |
| 126 | + searchParam: '' | |
| 127 | + } | |
| 128 | + }, | |
| 129 | + | |
| 130 | + onLoad() { | |
| 131 | + this.checkLoginStatus() | |
| 132 | + this.loadProductTypeOptions() | |
| 133 | + }, | |
| 134 | + | |
| 135 | + methods: { | |
| 136 | + // 检查登录状态 | |
| 137 | + checkLoginStatus() { | |
| 138 | + const token = uni.getStorageSync('token') | |
| 139 | + if (!token) { | |
| 140 | + uni.reLaunch({ | |
| 141 | + url: '/pages/login/login' | |
| 142 | + }) | |
| 143 | + return | |
| 144 | + } | |
| 145 | + }, | |
| 146 | + | |
| 147 | + // 打开选择弹窗 | |
| 148 | + openSelectModal(field) { | |
| 149 | + this.currentSelectField = field | |
| 150 | + this.currentPage = 1 | |
| 151 | + this.hasMoreData = true | |
| 152 | + this.searchKeyword = '' | |
| 153 | + this.searchParam = '' | |
| 154 | + | |
| 155 | + if (field === 'store') { | |
| 156 | + this.modalTitle = '选择门店' | |
| 157 | + this.searchParam = '请输入门店名称' | |
| 158 | + } else if (field === 'supplier') { | |
| 159 | + this.modalTitle = '选择清洗商' | |
| 160 | + this.searchParam = '请输入清洗商名称' | |
| 161 | + } else if (field === 'productType') { | |
| 162 | + this.modalTitle = '选择产品类型' | |
| 163 | + this.searchParam = '请输入产品类型名称' | |
| 164 | + } | |
| 165 | + | |
| 166 | + this.showModal = true | |
| 167 | + this.loadOptionsData(field, 1) | |
| 168 | + }, | |
| 169 | + | |
| 170 | + // 加载选项数据 | |
| 171 | + async loadOptionsData(fieldId, page = 1, searchKeyword = '') { | |
| 172 | + this.modalLoading = true | |
| 173 | + let options = [] | |
| 174 | + | |
| 175 | + try { | |
| 176 | + if (fieldId === 'store') { | |
| 177 | + options = await this.getStoreOptions(page, searchKeyword) | |
| 178 | + } else if (fieldId === 'supplier') { | |
| 179 | + options = await this.getSupplierOptions(page, searchKeyword) | |
| 180 | + } else if (fieldId === 'productType') { | |
| 181 | + options = this.getProductTypeOptions(searchKeyword) | |
| 182 | + } | |
| 183 | + | |
| 184 | + if (page === 1) { | |
| 185 | + this.currentOptions = options | |
| 186 | + } else { | |
| 187 | + this.currentOptions = [...this.currentOptions, ...options] | |
| 188 | + } | |
| 189 | + | |
| 190 | + this.hasMoreData = options.length === this.pageSize | |
| 191 | + } catch (error) { | |
| 192 | + console.error('加载选项数据失败:', error) | |
| 193 | + this.currentOptions = [] | |
| 194 | + } finally { | |
| 195 | + this.modalLoading = false | |
| 196 | + } | |
| 197 | + }, | |
| 198 | + | |
| 199 | + // 获取门店选项 | |
| 200 | + async getStoreOptions(page = 1, searchKeyword = '') { | |
| 201 | + try { | |
| 202 | + const params = { | |
| 203 | + currentPage: page, | |
| 204 | + pageSize: this.pageSize | |
| 205 | + } | |
| 206 | + if (searchKeyword) { | |
| 207 | + params.dm = searchKeyword | |
| 208 | + } | |
| 209 | + | |
| 210 | + const result = await memberApi.getStoreList(params) | |
| 211 | + if (result.code === 200 && result.data) { | |
| 212 | + let storeList = [] | |
| 213 | + if (Array.isArray(result.data)) { | |
| 214 | + storeList = result.data | |
| 215 | + } else if (result.data.list) { | |
| 216 | + storeList = result.data.list | |
| 217 | + } | |
| 218 | + | |
| 219 | + const options = storeList.map(item => ({ | |
| 220 | + value: item.id, | |
| 221 | + label: item.dm || '未知门店', | |
| 222 | + fullName: item.dm, | |
| 223 | + id: item.id | |
| 224 | + })) | |
| 225 | + | |
| 226 | + return options | |
| 227 | + } | |
| 228 | + return [] | |
| 229 | + } catch (error) { | |
| 230 | + console.error('获取门店列表失败:', error) | |
| 231 | + return [] | |
| 232 | + } | |
| 233 | + }, | |
| 234 | + | |
| 235 | + // 获取清洗商选项 | |
| 236 | + async getSupplierOptions(page = 1, searchKeyword = '') { | |
| 237 | + try { | |
| 238 | + const params = { | |
| 239 | + currentPage: page, | |
| 240 | + pageSize: this.pageSize, | |
| 241 | + isEffective: 1 | |
| 242 | + } | |
| 243 | + if (searchKeyword) { | |
| 244 | + params.supplierName = searchKeyword | |
| 245 | + } | |
| 246 | + | |
| 247 | + const result = await laundryFlowApi.getSupplierList(params) | |
| 248 | + if (result.code === 200 && result.data) { | |
| 249 | + const list = result.data.list || [] | |
| 250 | + const options = list.map(item => ({ | |
| 251 | + value: item.id, | |
| 252 | + label: item.supplierName || '未知清洗商', | |
| 253 | + supplierName: item.supplierName, | |
| 254 | + productType: item.productType, | |
| 255 | + laundryPrice: item.laundryPrice, | |
| 256 | + id: item.id | |
| 257 | + })) | |
| 258 | + | |
| 259 | + return options | |
| 260 | + } | |
| 261 | + return [] | |
| 262 | + } catch (error) { | |
| 263 | + console.error('获取清洗商列表失败:', error) | |
| 264 | + return [] | |
| 265 | + } | |
| 266 | + }, | |
| 267 | + | |
| 268 | + // 加载产品类型选项 | |
| 269 | + async loadProductTypeOptions() { | |
| 270 | + try { | |
| 271 | + const result = await laundryFlowApi.getProductTypeList() | |
| 272 | + if (result.code === 200 && result.data && Array.isArray(result.data)) { | |
| 273 | + this.productTypeOptions = result.data | |
| 274 | + } else { | |
| 275 | + this.productTypeOptions = [] | |
| 276 | + } | |
| 277 | + } catch (error) { | |
| 278 | + console.error('获取产品类型列表失败:', error) | |
| 279 | + this.productTypeOptions = [] | |
| 280 | + } | |
| 281 | + }, | |
| 282 | + | |
| 283 | + // 获取产品类型选项 | |
| 284 | + getProductTypeOptions(searchKeyword = '') { | |
| 285 | + let list = this.productTypeOptions | |
| 286 | + | |
| 287 | + if (searchKeyword) { | |
| 288 | + list = list.filter(item => | |
| 289 | + item.Name && item.Name.includes(searchKeyword) | |
| 290 | + ) | |
| 291 | + } | |
| 292 | + | |
| 293 | + return list.map(item => ({ | |
| 294 | + value: item.Name, | |
| 295 | + label: item.Name || '未知产品类型', | |
| 296 | + Name: item.Name | |
| 297 | + })) | |
| 298 | + }, | |
| 299 | + | |
| 300 | + // 处理弹窗确认 | |
| 301 | + handleModalConfirm(item) { | |
| 302 | + if (this.currentSelectField === 'store') { | |
| 303 | + this.formData.storeId = item.value | |
| 304 | + this.formData.storeName = item.label | |
| 305 | + } else if (this.currentSelectField === 'productType') { | |
| 306 | + this.formData.productType = item.value | |
| 307 | + // 验证产品类型是否匹配已选择的清洗商 | |
| 308 | + if (this.selectedSupplier && this.selectedSupplier.productType && this.formData.productType !== this.selectedSupplier.productType) { | |
| 309 | + uni.showToast({ | |
| 310 | + title: `清洗商【${this.selectedSupplier.supplierName}】不支持清洗产品类型【${this.formData.productType}】`, | |
| 311 | + icon: 'none', | |
| 312 | + duration: 3000 | |
| 313 | + }) | |
| 314 | + } | |
| 315 | + } else if (this.currentSelectField === 'supplier') { | |
| 316 | + this.formData.laundrySupplierId = item.value | |
| 317 | + this.formData.supplierName = item.label | |
| 318 | + this.selectedSupplier = item | |
| 319 | + // 自动填充清洗单价 | |
| 320 | + this.formData.laundryPrice = item.laundryPrice || '' | |
| 321 | + // 验证产品类型是否匹配 | |
| 322 | + if (this.formData.productType && item.productType && this.formData.productType !== item.productType) { | |
| 323 | + uni.showToast({ | |
| 324 | + title: `清洗商【${item.supplierName}】不支持清洗产品类型【${this.formData.productType}】`, | |
| 325 | + icon: 'none', | |
| 326 | + duration: 3000 | |
| 327 | + }) | |
| 328 | + } | |
| 329 | + } | |
| 330 | + this.closeModal() | |
| 331 | + }, | |
| 332 | + | |
| 333 | + // 关闭弹窗 | |
| 334 | + closeModal() { | |
| 335 | + this.showModal = false | |
| 336 | + this.currentSelectField = '' | |
| 337 | + this.currentOptions = [] | |
| 338 | + this.modalLoading = false | |
| 339 | + this.hasMoreData = true | |
| 340 | + this.currentPage = 1 | |
| 341 | + this.searchKeyword = '' | |
| 342 | + }, | |
| 343 | + | |
| 344 | + // 处理加载更多 | |
| 345 | + async handleLoadMore() { | |
| 346 | + if (!this.hasMoreData || this.modalLoading) return | |
| 347 | + this.currentPage++ | |
| 348 | + await this.loadOptionsData(this.currentSelectField, this.currentPage, this.searchKeyword) | |
| 349 | + }, | |
| 350 | + | |
| 351 | + // 处理刷新 | |
| 352 | + async handleRefresh() { | |
| 353 | + this.currentPage = 1 | |
| 354 | + this.hasMoreData = true | |
| 355 | + await this.loadOptionsData(this.currentSelectField, 1, this.searchKeyword) | |
| 356 | + }, | |
| 357 | + | |
| 358 | + // 处理搜索 | |
| 359 | + async handleSearch(keyword) { | |
| 360 | + this.searchKeyword = keyword | |
| 361 | + this.currentPage = 1 | |
| 362 | + this.hasMoreData = true | |
| 363 | + await this.loadOptionsData(this.currentSelectField, 1, keyword) | |
| 364 | + }, | |
| 365 | + | |
| 366 | + // 表单提交 | |
| 367 | + async handleFormSubmit() { | |
| 368 | + // 验证必填字段 | |
| 369 | + if (!this.formData.storeId) { | |
| 370 | + uni.showToast({ | |
| 371 | + title: '请选择门店', | |
| 372 | + icon: 'none' | |
| 373 | + }) | |
| 374 | + return | |
| 375 | + } | |
| 376 | + | |
| 377 | + if (!this.formData.productType) { | |
| 378 | + uni.showToast({ | |
| 379 | + title: '请选择产品类型', | |
| 380 | + icon: 'none' | |
| 381 | + }) | |
| 382 | + return | |
| 383 | + } | |
| 384 | + | |
| 385 | + if (!this.formData.laundrySupplierId) { | |
| 386 | + uni.showToast({ | |
| 387 | + title: '请选择清洗商', | |
| 388 | + icon: 'none' | |
| 389 | + }) | |
| 390 | + return | |
| 391 | + } | |
| 392 | + | |
| 393 | + // 验证产品类型是否匹配 | |
| 394 | + if (this.selectedSupplier && this.formData.productType !== this.selectedSupplier.productType) { | |
| 395 | + uni.showToast({ | |
| 396 | + title: `清洗商【${this.selectedSupplier.supplierName}】不支持清洗产品类型【${this.formData.productType}】`, | |
| 397 | + icon: 'none', | |
| 398 | + duration: 3000 | |
| 399 | + }) | |
| 400 | + return | |
| 401 | + } | |
| 402 | + | |
| 403 | + if (!this.formData.quantity || this.formData.quantity <= 0) { | |
| 404 | + uni.showToast({ | |
| 405 | + title: '请输入有效的送出数量', | |
| 406 | + icon: 'none' | |
| 407 | + }) | |
| 408 | + return | |
| 409 | + } | |
| 410 | + | |
| 411 | + this.isSubmitting = true | |
| 412 | + | |
| 413 | + try { | |
| 414 | + uni.showLoading({ | |
| 415 | + title: '提交中...' | |
| 416 | + }) | |
| 417 | + | |
| 418 | + const submitData = { | |
| 419 | + storeId: this.formData.storeId, | |
| 420 | + productType: this.formData.productType, | |
| 421 | + laundrySupplierId: this.formData.laundrySupplierId, | |
| 422 | + quantity: parseInt(this.formData.quantity), | |
| 423 | + remark: this.formData.remark || '' | |
| 424 | + } | |
| 425 | + | |
| 426 | + const res = await laundryFlowApi.createSendRecord(submitData) | |
| 427 | + | |
| 428 | + if (res.code === 200) { | |
| 429 | + uni.showToast({ | |
| 430 | + title: res.msg || '创建成功', | |
| 431 | + icon: 'success' | |
| 432 | + }) | |
| 433 | + setTimeout(() => { | |
| 434 | + uni.navigateBack() | |
| 435 | + }, 1500) | |
| 436 | + } else { | |
| 437 | + throw new Error(res.msg || res.message || '创建失败') | |
| 438 | + } | |
| 439 | + } catch (error) { | |
| 440 | + console.error('提交失败:', error) | |
| 441 | + uni.showToast({ | |
| 442 | + title: error.message || error.msg || '创建失败,请重试', | |
| 443 | + icon: 'none', | |
| 444 | + duration: 3000 | |
| 445 | + }) | |
| 446 | + } finally { | |
| 447 | + this.isSubmitting = false | |
| 448 | + uni.hideLoading() | |
| 449 | + } | |
| 450 | + } | |
| 451 | + } | |
| 452 | + } | |
| 453 | +</script> | |
| 454 | + | |
| 455 | +<style lang="scss" scoped> | |
| 456 | + .form-container { | |
| 457 | + min-height: 100vh; | |
| 458 | + background: linear-gradient(135deg, #e8f5e9 0%, #b2dfdb 100%); | |
| 459 | + padding: 40rpx; | |
| 460 | + box-sizing: border-box; | |
| 461 | + } | |
| 462 | + | |
| 463 | + .form-card { | |
| 464 | + background: #fff; | |
| 465 | + border-radius: 32rpx; | |
| 466 | + box-shadow: 0 8rpx 32rpx rgba(76, 175, 80, 0.1); | |
| 467 | + overflow: hidden; | |
| 468 | + } | |
| 469 | + | |
| 470 | + .form-content { | |
| 471 | + padding: 40rpx; | |
| 472 | + } | |
| 473 | + | |
| 474 | + .form-group { | |
| 475 | + margin-bottom: 40rpx; | |
| 476 | + } | |
| 477 | + | |
| 478 | + .form-label { | |
| 479 | + display: block; | |
| 480 | + font-size: 28rpx; | |
| 481 | + color: #2e7d32; | |
| 482 | + margin-bottom: 16rpx; | |
| 483 | + font-weight: 500; | |
| 484 | + } | |
| 485 | + | |
| 486 | + .input-wrapper { | |
| 487 | + width: 100%; | |
| 488 | + } | |
| 489 | + | |
| 490 | + .custom-select { | |
| 491 | + display: flex; | |
| 492 | + align-items: center; | |
| 493 | + justify-content: space-between; | |
| 494 | + background: #f9fff9; | |
| 495 | + border: 3rpx solid #c8e6c9; | |
| 496 | + border-radius: 24rpx; | |
| 497 | + padding: 24rpx 32rpx; | |
| 498 | + min-height: 88rpx; | |
| 499 | + box-sizing: border-box; | |
| 500 | + } | |
| 501 | + | |
| 502 | + .select-text { | |
| 503 | + flex: 1; | |
| 504 | + font-size: 28rpx; | |
| 505 | + color: #2e7d32; | |
| 506 | + } | |
| 507 | + | |
| 508 | + .select-text:empty::before { | |
| 509 | + content: '请选择'; | |
| 510 | + color: #999; | |
| 511 | + } | |
| 512 | + | |
| 513 | + .select-arrow { | |
| 514 | + font-size: 24rpx; | |
| 515 | + color: #6a9c6a; | |
| 516 | + margin-left: 16rpx; | |
| 517 | + } | |
| 518 | + | |
| 519 | + .select-input { | |
| 520 | + background: #f9fff9; | |
| 521 | + border: 3rpx solid #c8e6c9; | |
| 522 | + border-radius: 24rpx; | |
| 523 | + padding: 24rpx 32rpx; | |
| 524 | + font-size: 28rpx; | |
| 525 | + color: #2e7d32; | |
| 526 | + min-height: 88rpx; | |
| 527 | + box-sizing: border-box; | |
| 528 | + } | |
| 529 | + | |
| 530 | + .btn-group { | |
| 531 | + margin-top: 60rpx; | |
| 532 | + } | |
| 533 | + | |
| 534 | + .btn { | |
| 535 | + width: 100%; | |
| 536 | + height: 96rpx; | |
| 537 | + border-radius: 32rpx; | |
| 538 | + font-size: 32rpx; | |
| 539 | + font-weight: 600; | |
| 540 | + border: none; | |
| 541 | + display: flex; | |
| 542 | + align-items: center; | |
| 543 | + justify-content: center; | |
| 544 | + letter-spacing: 2rpx; | |
| 545 | + } | |
| 546 | + | |
| 547 | + .btn-primary { | |
| 548 | + background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); | |
| 549 | + color: #fff; | |
| 550 | + box-shadow: 0 8rpx 24rpx rgba(67, 233, 123, 0.3); | |
| 551 | + } | |
| 552 | + | |
| 553 | + .btn-primary:active { | |
| 554 | + transform: scale(0.98); | |
| 555 | + box-shadow: 0 4rpx 12rpx rgba(67, 233, 123, 0.4); | |
| 556 | + } | |
| 557 | +</style> | |
| 558 | + | ... | ... |
绿纤uni-app/pages/usage-form/usage-form.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="form-container"> | |
| 3 | + <view class="form-card"> | |
| 4 | + <view class="form-content"> | |
| 5 | + <!-- 使用记录列表 --> | |
| 6 | + <view v-for="(item, index) in usageItems" :key="index" class="usage-item-card"> | |
| 7 | + <view class="item-header"> | |
| 8 | + <text class="item-title">使用记录 {{ index + 1 }}</text> | |
| 9 | + <view v-if="usageItems.length > 1" class="delete-btn" @click="removeItem(index)"> | |
| 10 | + <text class="delete-icon">🗑️</text> | |
| 11 | + </view> | |
| 12 | + </view> | |
| 13 | + | |
| 14 | + <!-- 产品 --> | |
| 15 | + <view class="form-group"> | |
| 16 | + <text class="form-label">产品</text> | |
| 17 | + <view class="input-wrapper"> | |
| 18 | + <view class="custom-select" @tap="openSelectModal('product', index)"> | |
| 19 | + <text class="select-text">{{ item.productName || '请选择产品' }}</text> | |
| 20 | + <text class="select-arrow">▼</text> | |
| 21 | + </view> | |
| 22 | + </view> | |
| 23 | + </view> | |
| 24 | + | |
| 25 | + <!-- 门店 --> | |
| 26 | + <view class="form-group"> | |
| 27 | + <text class="form-label">门店</text> | |
| 28 | + <view class="input-wrapper"> | |
| 29 | + <view class="custom-select" @tap="openSelectModal('store', index)"> | |
| 30 | + <text class="select-text">{{ item.storeName || '请选择门店' }}</text> | |
| 31 | + <text class="select-arrow">▼</text> | |
| 32 | + </view> | |
| 33 | + </view> | |
| 34 | + </view> | |
| 35 | + | |
| 36 | + <!-- 使用数量 --> | |
| 37 | + <view class="form-group"> | |
| 38 | + <text class="form-label">使用数量</text> | |
| 39 | + <view class="input-wrapper"> | |
| 40 | + <u-input v-model="item.usageQuantity" placeholder="请输入使用数量" type="number" | |
| 41 | + class="select-input" /> | |
| 42 | + </view> | |
| 43 | + </view> | |
| 44 | + | |
| 45 | + <!-- 使用时间 --> | |
| 46 | + <view class="form-group"> | |
| 47 | + <text class="form-label">使用时间</text> | |
| 48 | + <view class="input-wrapper"> | |
| 49 | + <view class="custom-select" @tap="openDateTimePicker(index)"> | |
| 50 | + <text class="select-text">{{ item.usageTimeStr || '请选择使用时间' }}</text> | |
| 51 | + <text class="select-arrow">▼</text> | |
| 52 | + </view> | |
| 53 | + </view> | |
| 54 | + </view> | |
| 55 | + | |
| 56 | + <!-- 分隔线 --> | |
| 57 | + <view v-if="index < usageItems.length - 1" class="divider"></view> | |
| 58 | + </view> | |
| 59 | + | |
| 60 | + <!-- 添加记录按钮 --> | |
| 61 | + <view class="add-item-btn" @click="addItem"> | |
| 62 | + <text class="add-text">添加一条记录</text> | |
| 63 | + </view> | |
| 64 | + | |
| 65 | + <!-- 提交按钮 --> | |
| 66 | + <view class="btn-group"> | |
| 67 | + <button type="submit" class="btn btn-primary" | |
| 68 | + :style="{opacity: isSubmitting ? 0.5 : 1}" | |
| 69 | + @tap="isSubmitting ? null : handleFormSubmit()"> | |
| 70 | + {{ isSubmitting ? '提交中...' : '提交' }} | |
| 71 | + </button> | |
| 72 | + </view> | |
| 73 | + </view> | |
| 74 | + </view> | |
| 75 | + | |
| 76 | + <!-- 选择弹窗 --> | |
| 77 | + <SearchSelectModal | |
| 78 | + :show="showModal" | |
| 79 | + :title="modalTitle" | |
| 80 | + :options="currentOptions" | |
| 81 | + :loading="modalLoading" | |
| 82 | + :has-more="hasMoreData" | |
| 83 | + :search-param="searchParam" | |
| 84 | + @confirm="handleModalConfirm" | |
| 85 | + @close="closeModal" | |
| 86 | + @load-more="handleLoadMore" | |
| 87 | + @refresh="handleRefresh" | |
| 88 | + @search="handleSearch" /> | |
| 89 | + | |
| 90 | + <!-- 日期时间选择器 --> | |
| 91 | + <u-datetime-picker | |
| 92 | + v-if="showDateTimePicker" | |
| 93 | + :show="showDateTimePicker" | |
| 94 | + :value="currentDateTimeValue" | |
| 95 | + mode="datetime" | |
| 96 | + @confirm="onDateTimeConfirm" | |
| 97 | + @close="closeDateTimePicker" | |
| 98 | + @cancel="closeDateTimePicker" | |
| 99 | + :show-toolbar="true" | |
| 100 | + :close-on-click-overlay="true" | |
| 101 | + confirm-color="#43e97b" | |
| 102 | + title="选择使用时间" | |
| 103 | + class="datetime-picker" /> | |
| 104 | + </view> | |
| 105 | +</template> | |
| 106 | + | |
| 107 | +<script> | |
| 108 | + import SearchSelectModal from '@/components/SearchSelectModal.vue' | |
| 109 | + import usageApi from '@/apis/modules/usage.js' | |
| 110 | + import memberApi from '@/apis/modules/member.js' | |
| 111 | + import request from '@/service/request.js' | |
| 112 | + import config from '@/common/config.js' | |
| 113 | + | |
| 114 | + export default { | |
| 115 | + components: { | |
| 116 | + SearchSelectModal | |
| 117 | + }, | |
| 118 | + data() { | |
| 119 | + return { | |
| 120 | + isSubmitting: false, | |
| 121 | + usageItems: [ | |
| 122 | + { | |
| 123 | + productId: '', | |
| 124 | + productName: '', | |
| 125 | + storeId: '', | |
| 126 | + storeName: '', | |
| 127 | + usageTime: '', | |
| 128 | + usageTimeStr: '', | |
| 129 | + usageQuantity: 1, | |
| 130 | + relatedConsumeId: '' | |
| 131 | + } | |
| 132 | + ], | |
| 133 | + // 选择弹窗相关 | |
| 134 | + showModal: false, | |
| 135 | + modalTitle: '', | |
| 136 | + currentSelectField: '', | |
| 137 | + currentSelectIndex: 0, | |
| 138 | + currentOptions: [], | |
| 139 | + modalLoading: false, | |
| 140 | + hasMoreData: true, | |
| 141 | + currentPage: 1, | |
| 142 | + pageSize: 20, | |
| 143 | + searchKeyword: '', | |
| 144 | + searchParam: '', | |
| 145 | + // 用户信息 | |
| 146 | + userInfo: null, | |
| 147 | + newuserInfo: null, | |
| 148 | + // 日期时间选择器相关 | |
| 149 | + showDateTimePicker: false, | |
| 150 | + currentDateTimeIndex: -1, | |
| 151 | + currentDateTimeValue: 0 | |
| 152 | + } | |
| 153 | + }, | |
| 154 | + | |
| 155 | + onLoad() { | |
| 156 | + this.initializePage() | |
| 157 | + }, | |
| 158 | + | |
| 159 | + methods: { | |
| 160 | + // 初始化页面 | |
| 161 | + async initializePage() { | |
| 162 | + try { | |
| 163 | + // 获取用户信息 | |
| 164 | + this.userInfo = uni.getStorageSync('userInfo') | |
| 165 | + this.newuserInfo = uni.getStorageSync('newuserInfo') | |
| 166 | + | |
| 167 | + if (!this.userInfo || Object.keys(this.userInfo).length === 0) { | |
| 168 | + uni.showToast({ | |
| 169 | + title: '请先登录', | |
| 170 | + icon: 'none' | |
| 171 | + }) | |
| 172 | + setTimeout(() => { | |
| 173 | + uni.reLaunch({ | |
| 174 | + url: '/pages/login/login' | |
| 175 | + }) | |
| 176 | + }, 1500) | |
| 177 | + return | |
| 178 | + } | |
| 179 | + | |
| 180 | + // 设置默认值 | |
| 181 | + this.setDefaultValues() | |
| 182 | + } catch (error) { | |
| 183 | + console.error('页面初始化失败:', error) | |
| 184 | + uni.showToast({ | |
| 185 | + title: '页面初始化失败', | |
| 186 | + icon: 'none' | |
| 187 | + }) | |
| 188 | + } | |
| 189 | + }, | |
| 190 | + | |
| 191 | + // 设置默认值 | |
| 192 | + setDefaultValues() { | |
| 193 | + // 使用时间:默认当前时间 | |
| 194 | + const now = new Date() | |
| 195 | + this.usageItems.forEach(item => { | |
| 196 | + if (!item.usageTimeStr) { | |
| 197 | + item.usageTime = now.getTime() | |
| 198 | + item.usageTimeStr = this.formatDateTime(now) | |
| 199 | + } | |
| 200 | + }) | |
| 201 | + }, | |
| 202 | + | |
| 203 | + // 格式化日期时间 | |
| 204 | + formatDateTime(date) { | |
| 205 | + const year = date.getFullYear() | |
| 206 | + const month = String(date.getMonth() + 1).padStart(2, '0') | |
| 207 | + const day = String(date.getDate()).padStart(2, '0') | |
| 208 | + const hours = String(date.getHours()).padStart(2, '0') | |
| 209 | + const minutes = String(date.getMinutes()).padStart(2, '0') | |
| 210 | + return `${year}-${month}-${day} ${hours}:${minutes}` | |
| 211 | + }, | |
| 212 | + | |
| 213 | + // 打开选择弹窗 | |
| 214 | + async openSelectModal(fieldId, index) { | |
| 215 | + this.currentSelectField = fieldId | |
| 216 | + this.currentSelectIndex = index | |
| 217 | + this.showModal = true | |
| 218 | + this.modalTitle = '加载中...' | |
| 219 | + this.modalLoading = true | |
| 220 | + this.currentPage = 1 | |
| 221 | + this.hasMoreData = true | |
| 222 | + this.searchKeyword = '' | |
| 223 | + this.currentOptions = [] | |
| 224 | + | |
| 225 | + try { | |
| 226 | + if (fieldId === 'product') { | |
| 227 | + this.searchParam = 'productName' | |
| 228 | + this.modalTitle = '选择产品' | |
| 229 | + } else if (fieldId === 'store') { | |
| 230 | + this.searchParam = 'fullName' | |
| 231 | + this.modalTitle = '选择门店' | |
| 232 | + } | |
| 233 | + | |
| 234 | + await this.loadOptionsData(fieldId, 1) | |
| 235 | + } catch (error) { | |
| 236 | + console.error('获取选项数据失败:', error) | |
| 237 | + this.modalTitle = '加载失败' | |
| 238 | + this.currentOptions = [] | |
| 239 | + uni.showToast({ | |
| 240 | + title: '数据加载失败,请检查网络连接', | |
| 241 | + icon: 'none' | |
| 242 | + }) | |
| 243 | + } finally { | |
| 244 | + this.modalLoading = false | |
| 245 | + } | |
| 246 | + }, | |
| 247 | + | |
| 248 | + // 关闭弹窗 | |
| 249 | + closeModal() { | |
| 250 | + this.showModal = false | |
| 251 | + this.currentSelectField = '' | |
| 252 | + this.currentSelectIndex = 0 | |
| 253 | + this.currentOptions = [] | |
| 254 | + this.modalLoading = false | |
| 255 | + this.hasMoreData = true | |
| 256 | + this.currentPage = 1 | |
| 257 | + this.searchKeyword = '' | |
| 258 | + }, | |
| 259 | + | |
| 260 | + // 加载选项数据 | |
| 261 | + async loadOptionsData(fieldId, page = 1, searchKeyword = '') { | |
| 262 | + let options = [] | |
| 263 | + | |
| 264 | + try { | |
| 265 | + if (fieldId === 'product') { | |
| 266 | + // 加载产品列表(只获取上架的产品) | |
| 267 | + const params = { | |
| 268 | + currentPage: page, | |
| 269 | + pageSize: this.pageSize, | |
| 270 | + onShelfStatus: 1 | |
| 271 | + } | |
| 272 | + if (searchKeyword) { | |
| 273 | + params.productName = searchKeyword | |
| 274 | + } | |
| 275 | + | |
| 276 | + const res = await request.get(`${config.getApiBaseUrl()}/api/Extend/LqProduct/GetList`, params) | |
| 277 | + | |
| 278 | + if (res.code === 200 && res.data) { | |
| 279 | + const list = res.data.list || [] | |
| 280 | + options = list.map(item => ({ | |
| 281 | + value: item.id, | |
| 282 | + label: item.productName || '未知产品', | |
| 283 | + productName: item.productName, | |
| 284 | + id: item.id | |
| 285 | + })) | |
| 286 | + } | |
| 287 | + } else if (fieldId === 'store') { | |
| 288 | + // 加载门店列表 | |
| 289 | + options = await this.getStoreOptions(page, searchKeyword) | |
| 290 | + } | |
| 291 | + | |
| 292 | + if (page === 1) { | |
| 293 | + this.currentOptions = options | |
| 294 | + } else { | |
| 295 | + this.currentOptions = [...this.currentOptions, ...options] | |
| 296 | + } | |
| 297 | + | |
| 298 | + this.hasMoreData = options.length === this.pageSize | |
| 299 | + } catch (error) { | |
| 300 | + console.error('加载选项数据失败:', error) | |
| 301 | + this.currentOptions = [] | |
| 302 | + } | |
| 303 | + }, | |
| 304 | + | |
| 305 | + // 获取门店选项 | |
| 306 | + async getStoreOptions(page = 1, searchKeyword = '') { | |
| 307 | + try { | |
| 308 | + const params = { | |
| 309 | + currentPage: page, | |
| 310 | + pageSize: this.pageSize | |
| 311 | + } | |
| 312 | + if (searchKeyword) { | |
| 313 | + params.dm = searchKeyword | |
| 314 | + } | |
| 315 | + | |
| 316 | + const result = await memberApi.getStoreList(params) | |
| 317 | + if (result.code === 200 && result.data) { | |
| 318 | + // 处理分页数据 | |
| 319 | + let storeList = [] | |
| 320 | + if (Array.isArray(result.data)) { | |
| 321 | + storeList = result.data | |
| 322 | + } else if (result.data.list) { | |
| 323 | + storeList = result.data.list | |
| 324 | + } | |
| 325 | + | |
| 326 | + const options = storeList.map(item => ({ | |
| 327 | + value: item.id, | |
| 328 | + label: item.dm || '未知门店', | |
| 329 | + fullName: item.dm, | |
| 330 | + id: item.id | |
| 331 | + })) | |
| 332 | + | |
| 333 | + return options | |
| 334 | + } | |
| 335 | + return [] | |
| 336 | + } catch (error) { | |
| 337 | + console.error('获取门店列表失败:', error) | |
| 338 | + return [] | |
| 339 | + } | |
| 340 | + }, | |
| 341 | + | |
| 342 | + // 处理弹窗确认 | |
| 343 | + handleModalConfirm(item) { | |
| 344 | + const index = this.currentSelectIndex | |
| 345 | + if (this.currentSelectField === 'product') { | |
| 346 | + this.usageItems[index].productId = item.value | |
| 347 | + this.usageItems[index].productName = item.label | |
| 348 | + } else if (this.currentSelectField === 'store') { | |
| 349 | + this.usageItems[index].storeId = item.value | |
| 350 | + this.usageItems[index].storeName = item.label | |
| 351 | + } | |
| 352 | + this.closeModal() | |
| 353 | + }, | |
| 354 | + | |
| 355 | + // 处理加载更多 | |
| 356 | + async handleLoadMore() { | |
| 357 | + if (!this.hasMoreData || this.modalLoading) return | |
| 358 | + this.currentPage++ | |
| 359 | + await this.loadOptionsData(this.currentSelectField, this.currentPage, this.searchKeyword) | |
| 360 | + }, | |
| 361 | + | |
| 362 | + // 处理刷新 | |
| 363 | + async handleRefresh() { | |
| 364 | + this.currentPage = 1 | |
| 365 | + this.hasMoreData = true | |
| 366 | + await this.loadOptionsData(this.currentSelectField, 1, this.searchKeyword) | |
| 367 | + }, | |
| 368 | + | |
| 369 | + // 处理搜索 | |
| 370 | + async handleSearch(keyword) { | |
| 371 | + this.searchKeyword = keyword | |
| 372 | + this.currentPage = 1 | |
| 373 | + this.hasMoreData = true | |
| 374 | + await this.loadOptionsData(this.currentSelectField, 1, keyword) | |
| 375 | + }, | |
| 376 | + | |
| 377 | + // 打开日期时间选择器 | |
| 378 | + openDateTimePicker(index) { | |
| 379 | + this.currentDateTimeIndex = index | |
| 380 | + const item = this.usageItems[index] | |
| 381 | + this.currentDateTimeValue = item.usageTime || new Date().getTime() | |
| 382 | + this.showDateTimePicker = true | |
| 383 | + }, | |
| 384 | + | |
| 385 | + // 日期时间确认 | |
| 386 | + onDateTimeConfirm(e) { | |
| 387 | + const timestamp = e.value | |
| 388 | + const index = this.currentDateTimeIndex | |
| 389 | + if (index >= 0 && index < this.usageItems.length) { | |
| 390 | + const date = new Date(timestamp) | |
| 391 | + this.usageItems[index].usageTime = timestamp | |
| 392 | + this.usageItems[index].usageTimeStr = this.formatDateTime(date) | |
| 393 | + } | |
| 394 | + this.closeDateTimePicker() | |
| 395 | + }, | |
| 396 | + | |
| 397 | + // 关闭日期时间选择器 | |
| 398 | + closeDateTimePicker() { | |
| 399 | + this.showDateTimePicker = false | |
| 400 | + this.currentDateTimeIndex = -1 | |
| 401 | + this.currentDateTimeValue = 0 | |
| 402 | + }, | |
| 403 | + | |
| 404 | + // 添加记录 | |
| 405 | + addItem() { | |
| 406 | + const now = new Date() | |
| 407 | + this.usageItems.push({ | |
| 408 | + productId: '', | |
| 409 | + productName: '', | |
| 410 | + storeId: '', | |
| 411 | + storeName: '', | |
| 412 | + usageTime: now.getTime(), | |
| 413 | + usageTimeStr: this.formatDateTime(now), | |
| 414 | + usageQuantity: 1, | |
| 415 | + relatedConsumeId: '' | |
| 416 | + }) | |
| 417 | + }, | |
| 418 | + | |
| 419 | + // 删除记录 | |
| 420 | + removeItem(index) { | |
| 421 | + if (this.usageItems.length <= 1) { | |
| 422 | + uni.showToast({ | |
| 423 | + title: '至少保留一条记录', | |
| 424 | + icon: 'none' | |
| 425 | + }) | |
| 426 | + return | |
| 427 | + } | |
| 428 | + this.usageItems.splice(index, 1) | |
| 429 | + }, | |
| 430 | + | |
| 431 | + // 表单提交 | |
| 432 | + async handleFormSubmit() { | |
| 433 | + // 验证所有表单项 | |
| 434 | + let isValid = true | |
| 435 | + let errorMessage = '' | |
| 436 | + | |
| 437 | + this.usageItems.forEach((item, index) => { | |
| 438 | + if (!item.productId) { | |
| 439 | + errorMessage = `第${index + 1}条记录:请选择产品` | |
| 440 | + isValid = false | |
| 441 | + } | |
| 442 | + if (!item.storeId) { | |
| 443 | + errorMessage = `第${index + 1}条记录:请选择门店` | |
| 444 | + isValid = false | |
| 445 | + } | |
| 446 | + if (!item.usageQuantity || item.usageQuantity <= 0) { | |
| 447 | + errorMessage = `第${index + 1}条记录:请输入使用数量` | |
| 448 | + isValid = false | |
| 449 | + } | |
| 450 | + if (!item.usageTime) { | |
| 451 | + errorMessage = `第${index + 1}条记录:请选择使用时间` | |
| 452 | + isValid = false | |
| 453 | + } | |
| 454 | + }) | |
| 455 | + | |
| 456 | + if (!isValid) { | |
| 457 | + uni.showToast({ | |
| 458 | + title: errorMessage, | |
| 459 | + icon: 'none', | |
| 460 | + duration: 3000 | |
| 461 | + }) | |
| 462 | + return | |
| 463 | + } | |
| 464 | + | |
| 465 | + if (this.isSubmitting) return | |
| 466 | + | |
| 467 | + try { | |
| 468 | + this.isSubmitting = true | |
| 469 | + uni.showLoading({ | |
| 470 | + title: '提交中...' | |
| 471 | + }) | |
| 472 | + | |
| 473 | + // 转换日期时间为ISO格式 | |
| 474 | + const usageItems = this.usageItems.map(item => { | |
| 475 | + let usageTime = item.usageTime | |
| 476 | + // 如果日期时间不是ISO格式,转换为ISO格式 | |
| 477 | + if (usageTime && typeof usageTime === 'number') { | |
| 478 | + const date = new Date(usageTime) | |
| 479 | + usageTime = date.toISOString() | |
| 480 | + } | |
| 481 | + return { | |
| 482 | + productId: item.productId, | |
| 483 | + storeId: item.storeId, | |
| 484 | + usageTime: usageTime, | |
| 485 | + usageQuantity: item.usageQuantity, | |
| 486 | + relatedConsumeId: item.relatedConsumeId || '' | |
| 487 | + } | |
| 488 | + }) | |
| 489 | + | |
| 490 | + const res = await usageApi.batchCreateUsage({ | |
| 491 | + usageItems: usageItems | |
| 492 | + }) | |
| 493 | + | |
| 494 | + if (res.code === 200) { | |
| 495 | + uni.showToast({ | |
| 496 | + title: res.msg || res.message || '添加成功', | |
| 497 | + icon: 'success' | |
| 498 | + }) | |
| 499 | + setTimeout(() => { | |
| 500 | + uni.navigateBack() | |
| 501 | + }, 1500) | |
| 502 | + } else { | |
| 503 | + throw new Error(res.message || res.msg || '操作失败') | |
| 504 | + } | |
| 505 | + } catch (error) { | |
| 506 | + console.error('提交失败:', error) | |
| 507 | + uni.showToast({ | |
| 508 | + title: error.message || error.msg || '提交失败,请重试', | |
| 509 | + icon: 'none' | |
| 510 | + }) | |
| 511 | + } finally { | |
| 512 | + this.isSubmitting = false | |
| 513 | + uni.hideLoading() | |
| 514 | + } | |
| 515 | + } | |
| 516 | + } | |
| 517 | + } | |
| 518 | +</script> | |
| 519 | + | |
| 520 | +<style lang="scss" scoped> | |
| 521 | + .form-container { | |
| 522 | + min-height: 100vh; | |
| 523 | + background: linear-gradient(135deg, #e8f5e9 0%, #b2dfdb 100%); | |
| 524 | + padding: 40rpx; | |
| 525 | + box-sizing: border-box; | |
| 526 | + } | |
| 527 | + | |
| 528 | + .form-card { | |
| 529 | + background: #fff; | |
| 530 | + border-radius: 32rpx; | |
| 531 | + box-shadow: 0 8rpx 32rpx rgba(76, 175, 80, 0.1); | |
| 532 | + overflow: hidden; | |
| 533 | + } | |
| 534 | + | |
| 535 | + .form-content { | |
| 536 | + padding: 40rpx; | |
| 537 | + } | |
| 538 | + | |
| 539 | + .usage-item-card { | |
| 540 | + margin-bottom: 40rpx; | |
| 541 | + } | |
| 542 | + | |
| 543 | + .item-header { | |
| 544 | + display: flex; | |
| 545 | + justify-content: space-between; | |
| 546 | + align-items: center; | |
| 547 | + margin-bottom: 32rpx; | |
| 548 | + padding-bottom: 16rpx; | |
| 549 | + border-bottom: 2rpx solid #f0f0f0; | |
| 550 | + } | |
| 551 | + | |
| 552 | + .item-title { | |
| 553 | + font-size: 32rpx; | |
| 554 | + font-weight: 600; | |
| 555 | + color: #2e7d32; | |
| 556 | + } | |
| 557 | + | |
| 558 | + .delete-btn { | |
| 559 | + padding: 8rpx 16rpx; | |
| 560 | + border-radius: 16rpx; | |
| 561 | + background: #ffebee; | |
| 562 | + display: flex; | |
| 563 | + align-items: center; | |
| 564 | + justify-content: center; | |
| 565 | + } | |
| 566 | + | |
| 567 | + .delete-icon { | |
| 568 | + font-size: 28rpx; | |
| 569 | + } | |
| 570 | + | |
| 571 | + .form-group { | |
| 572 | + margin-bottom: 32rpx; | |
| 573 | + } | |
| 574 | + | |
| 575 | + .form-label { | |
| 576 | + display: block; | |
| 577 | + font-size: 28rpx; | |
| 578 | + color: #2e7d32; | |
| 579 | + margin-bottom: 16rpx; | |
| 580 | + font-weight: 500; | |
| 581 | + } | |
| 582 | + | |
| 583 | + .input-wrapper { | |
| 584 | + width: 100%; | |
| 585 | + } | |
| 586 | + | |
| 587 | + .custom-select { | |
| 588 | + display: flex; | |
| 589 | + align-items: center; | |
| 590 | + justify-content: space-between; | |
| 591 | + background: #f9fff9; | |
| 592 | + border: 3rpx solid #c8e6c9; | |
| 593 | + border-radius: 24rpx; | |
| 594 | + padding: 24rpx 32rpx; | |
| 595 | + min-height: 80rpx; | |
| 596 | + box-sizing: border-box; | |
| 597 | + } | |
| 598 | + | |
| 599 | + .select-text { | |
| 600 | + font-size: 28rpx; | |
| 601 | + color: #2e7d32; | |
| 602 | + flex: 1; | |
| 603 | + } | |
| 604 | + | |
| 605 | + .select-text:empty::before { | |
| 606 | + content: attr(placeholder); | |
| 607 | + color: #999; | |
| 608 | + } | |
| 609 | + | |
| 610 | + .select-arrow { | |
| 611 | + font-size: 24rpx; | |
| 612 | + color: #6a9c6a; | |
| 613 | + margin-left: 16rpx; | |
| 614 | + } | |
| 615 | + | |
| 616 | + .select-input { | |
| 617 | + background: #f9fff9; | |
| 618 | + border: 3rpx solid #c8e6c9; | |
| 619 | + border-radius: 24rpx; | |
| 620 | + padding: 24rpx 32rpx; | |
| 621 | + font-size: 28rpx; | |
| 622 | + color: #2e7d32; | |
| 623 | + } | |
| 624 | + | |
| 625 | + .divider { | |
| 626 | + height: 2rpx; | |
| 627 | + background: #f0f0f0; | |
| 628 | + margin: 40rpx 0; | |
| 629 | + } | |
| 630 | + | |
| 631 | + .add-item-btn { | |
| 632 | + display: flex; | |
| 633 | + align-items: center; | |
| 634 | + justify-content: center; | |
| 635 | + padding: 24rpx; | |
| 636 | + background: linear-gradient(135deg, #e8f5e9 0%, #c8e6c9 100%); | |
| 637 | + border: 3rpx dashed #43a047; | |
| 638 | + border-radius: 24rpx; | |
| 639 | + margin-bottom: 40rpx; | |
| 640 | + } | |
| 641 | + | |
| 642 | + .add-icon { | |
| 643 | + font-size: 32rpx; | |
| 644 | + margin-right: 12rpx; | |
| 645 | + } | |
| 646 | + | |
| 647 | + .add-text { | |
| 648 | + font-size: 28rpx; | |
| 649 | + color: #2e7d32; | |
| 650 | + font-weight: 500; | |
| 651 | + } | |
| 652 | + | |
| 653 | + .btn-group { | |
| 654 | + margin-top: 40rpx; | |
| 655 | + } | |
| 656 | + | |
| 657 | + .btn { | |
| 658 | + width: 100%; | |
| 659 | + padding:6rpx 28rpx; | |
| 660 | + border-radius: 32rpx; | |
| 661 | + font-size: 32rpx; | |
| 662 | + font-weight: 600; | |
| 663 | + letter-spacing: 2rpx; | |
| 664 | + border: none; | |
| 665 | + } | |
| 666 | + | |
| 667 | + .btn-primary { | |
| 668 | + background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); | |
| 669 | + color: #fff; | |
| 670 | + box-shadow: 0 8rpx 24rpx rgba(67, 233, 123, 0.3); | |
| 671 | + } | |
| 672 | + | |
| 673 | + .btn-primary:active { | |
| 674 | + transform: scale(0.98); | |
| 675 | + box-shadow: 0 4rpx 12rpx rgba(67, 233, 123, 0.4); | |
| 676 | + } | |
| 677 | +</style> | |
| 678 | + | ... | ... |
绿纤uni-app/pages/usage-list/usage-list.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="container"> | |
| 3 | + <!-- 日期范围筛选 --> | |
| 4 | + <view class="date-filter-card"> | |
| 5 | + <view class="date-filter-header"> | |
| 6 | + <view class="date-range-display" @click="showCalendarFun"> | |
| 7 | + <text class="date-text">{{ formatDateRange() }}</text> | |
| 8 | + <text class="calendar-icon">📅</text> | |
| 9 | + </view> | |
| 10 | + </view> | |
| 11 | + </view> | |
| 12 | + | |
| 13 | + <!-- 添加按钮 --> | |
| 14 | + <view class="add-btn-wrapper"> | |
| 15 | + <view class="add-btn" @click="goToAdd"> | |
| 16 | + <text class="add-text">添加使用记录</text> | |
| 17 | + </view> | |
| 18 | + </view> | |
| 19 | + | |
| 20 | + <!-- 日历组件 --> | |
| 21 | + <uni-calendar | |
| 22 | + :range="true" | |
| 23 | + ref="calendar" | |
| 24 | + @confirm="handleDateConfirm" | |
| 25 | + :insert="false" | |
| 26 | + ></uni-calendar> | |
| 27 | + | |
| 28 | + <!-- 数据列表 --> | |
| 29 | + <view class="list-card"> | |
| 30 | + <view class="list-header"> | |
| 31 | + <text class="header-text">使用记录</text> | |
| 32 | + <text class="total-count">共 {{ totalCount }} 条</text> | |
| 33 | + </view> | |
| 34 | + | |
| 35 | + <scroll-view scroll-y class="list-content" @scrolltolower="loadMore"> | |
| 36 | + <view v-for="(item, index) in dataList" :key="index" class="list-item"> | |
| 37 | + <view class="item-header"> | |
| 38 | + <view class="product-name">{{ item.productName || '无' }}</view> | |
| 39 | + <view class="usage-time">{{ formatTime(item.usageTime) }}</view> | |
| 40 | + </view> | |
| 41 | + <view class="item-details"> | |
| 42 | + <view class="detail-item"> | |
| 43 | + <text class="detail-label">门店:</text> | |
| 44 | + <text class="detail-value">{{ item.storeName || '无' }}</text> | |
| 45 | + </view> | |
| 46 | + <view class="detail-item"> | |
| 47 | + <text class="detail-label">使用数量:</text> | |
| 48 | + <text class="detail-value">{{ item.usageQuantity || 0 }}</text> | |
| 49 | + </view> | |
| 50 | + <view class="detail-item"> | |
| 51 | + <text class="detail-label">产品类别:</text> | |
| 52 | + <text class="detail-value">{{ item.productCategory || '无' }}</text> | |
| 53 | + </view> | |
| 54 | + <view class="detail-item"> | |
| 55 | + <text class="detail-label">创建人:</text> | |
| 56 | + <text class="detail-value">{{ item.createUserName || '无' }}</text> | |
| 57 | + </view> | |
| 58 | + </view> | |
| 59 | + <view class="item-footer"> | |
| 60 | + <view class="status-badge" :class="getStatusClass(item.isCanceled)"> | |
| 61 | + {{ item.isCanceled ? '已作废' : '正常' }} | |
| 62 | + </view> | |
| 63 | + <view class="action-buttons"> | |
| 64 | + <view v-if="!item.isCanceled" class="action-btn cancel-btn" @click.stop="handleCancel(item)"> | |
| 65 | + <text class="btn-text">作废</text> | |
| 66 | + </view> | |
| 67 | + </view> | |
| 68 | + </view> | |
| 69 | + </view> | |
| 70 | + | |
| 71 | + <!-- 空状态 --> | |
| 72 | + <view v-if="!loading && dataList.length === 0" class="empty-state"> | |
| 73 | + <view class="empty-icon">📋</view> | |
| 74 | + <view>暂无使用记录</view> | |
| 75 | + </view> | |
| 76 | + | |
| 77 | + <!-- 加载状态 --> | |
| 78 | + <view v-if="loading && dataList.length === 0" class="loading">正在加载数据...</view> | |
| 79 | + | |
| 80 | + <!-- 加载更多 --> | |
| 81 | + <u-loadmore v-if="dataList.length > 0" :status="loadmoreStatus" :load-text="loadText"></u-loadmore> | |
| 82 | + </scroll-view> | |
| 83 | + </view> | |
| 84 | + </view> | |
| 85 | +</template> | |
| 86 | + | |
| 87 | +<script> | |
| 88 | + import usageApi from '@/apis/modules/usage.js' | |
| 89 | + import uniCalendar from '@/uni_modules/uni-calendar/components/uni-calendar/uni-calendar.vue' | |
| 90 | + | |
| 91 | + export default { | |
| 92 | + components: { | |
| 93 | + uniCalendar | |
| 94 | + }, | |
| 95 | + data() { | |
| 96 | + // 初始化日期范围(本月1号到今天) | |
| 97 | + const now = new Date() | |
| 98 | + const year = now.getFullYear() | |
| 99 | + const month = now.getMonth() | |
| 100 | + const firstDay = new Date(year, month, 1) | |
| 101 | + const today = new Date(year, now.getMonth(), now.getDate(), 23, 59, 59, 999) | |
| 102 | + | |
| 103 | + // 格式化日期为 YYYY-MM-DD(使用本地时间,避免时区问题) | |
| 104 | + const formatDateLocal = (date) => { | |
| 105 | + const y = date.getFullYear() | |
| 106 | + const m = String(date.getMonth() + 1).padStart(2, '0') | |
| 107 | + const d = String(date.getDate()).padStart(2, '0') | |
| 108 | + return `${y}-${m}-${d}` | |
| 109 | + } | |
| 110 | + | |
| 111 | + return { | |
| 112 | + loading: false, | |
| 113 | + dataList: [], | |
| 114 | + currentPage: 1, | |
| 115 | + pageSize: 10, | |
| 116 | + totalCount: 0, | |
| 117 | + hasMore: true, | |
| 118 | + loadmoreStatus: 'loadmore', | |
| 119 | + loadText: { | |
| 120 | + loadmore: '点击或上拉加载更多', | |
| 121 | + loading: '正在加载...', | |
| 122 | + nomore: '没有更多了' | |
| 123 | + }, | |
| 124 | + userInfo: uni.getStorageSync('userInfo'), | |
| 125 | + newuserInfo: uni.getStorageSync('newuserInfo'), | |
| 126 | + // 日期范围筛选相关 | |
| 127 | + showCalendar: false, | |
| 128 | + startDate: formatDateLocal(firstDay), | |
| 129 | + endDate: formatDateLocal(now), | |
| 130 | + dateRange: [firstDay.getTime(), today.getTime()] | |
| 131 | + } | |
| 132 | + }, | |
| 133 | + | |
| 134 | + onLoad() { | |
| 135 | + this.initializePage() | |
| 136 | + }, | |
| 137 | + | |
| 138 | + onShow() { | |
| 139 | + // 页面显示时刷新数据(如果从添加页面返回) | |
| 140 | + if (this.dataList.length > 0) { | |
| 141 | + this.refreshData() | |
| 142 | + } | |
| 143 | + }, | |
| 144 | + | |
| 145 | + methods: { | |
| 146 | + showCalendarFun() { | |
| 147 | + this.$refs.calendar.open(); | |
| 148 | + }, | |
| 149 | + | |
| 150 | + // 初始化页面 | |
| 151 | + async initializePage() { | |
| 152 | + try { | |
| 153 | + // 检查登录状态 | |
| 154 | + await this.checkLoginStatus() | |
| 155 | + | |
| 156 | + // 加载第一页数据 | |
| 157 | + await this.loadUsageList() | |
| 158 | + } catch (error) { | |
| 159 | + console.error('页面初始化失败:', error) | |
| 160 | + this.showErrorState('页面初始化失败,请刷新重试') | |
| 161 | + } | |
| 162 | + }, | |
| 163 | + | |
| 164 | + // 检查登录状态 | |
| 165 | + async checkLoginStatus() { | |
| 166 | + const token = uni.getStorageSync('token') | |
| 167 | + if (!token) { | |
| 168 | + uni.reLaunch({ | |
| 169 | + url: '/pages/login/login' | |
| 170 | + }) | |
| 171 | + return | |
| 172 | + } | |
| 173 | + }, | |
| 174 | + | |
| 175 | + // 加载使用记录列表 | |
| 176 | + async loadUsageList() { | |
| 177 | + if (this.loading) return | |
| 178 | + | |
| 179 | + try { | |
| 180 | + this.loading = true | |
| 181 | + this.showLoadingState() | |
| 182 | + | |
| 183 | + const params = { | |
| 184 | + currentPage: this.currentPage, | |
| 185 | + pageSize: this.pageSize | |
| 186 | + } | |
| 187 | + | |
| 188 | + // 添加使用时间范围参数 | |
| 189 | + if (this.dateRange && this.dateRange.length === 2) { | |
| 190 | + params.usageTime = `${this.dateRange[0]},${this.dateRange[1]}` | |
| 191 | + } | |
| 192 | + | |
| 193 | + // if (this.userInfo && this.userInfo.userId) { | |
| 194 | + // params.createUser = this.userInfo.userId | |
| 195 | + // } else { | |
| 196 | + // params.createUser = '暂无' | |
| 197 | + // } | |
| 198 | + | |
| 199 | + params.StoreId = this.newuserInfo.mdid || '暂无' | |
| 200 | + const res = await usageApi.getUsageList(params) | |
| 201 | + | |
| 202 | + if (res.code === 200) { | |
| 203 | + const data = res.data | |
| 204 | + this.totalCount = data.pagination?.total || 0 | |
| 205 | + const list = data.list || [] | |
| 206 | + | |
| 207 | + if (this.currentPage === 1) { | |
| 208 | + // 第一页,清空列表 | |
| 209 | + this.dataList = list | |
| 210 | + } else { | |
| 211 | + // 加载更多,追加到列表 | |
| 212 | + this.dataList = [...this.dataList, ...list] | |
| 213 | + } | |
| 214 | + | |
| 215 | + // 判断是否还有更多数据 | |
| 216 | + this.hasMore = list.length === this.pageSize | |
| 217 | + | |
| 218 | + // 更新加载状态 | |
| 219 | + if (this.hasMore) { | |
| 220 | + this.loadmoreStatus = 'loadmore' | |
| 221 | + } else { | |
| 222 | + this.loadmoreStatus = 'nomore' | |
| 223 | + } | |
| 224 | + } else { | |
| 225 | + throw new Error(res.message || '获取数据失败') | |
| 226 | + } | |
| 227 | + | |
| 228 | + } catch (error) { | |
| 229 | + console.error('加载使用记录列表失败:', error) | |
| 230 | + if (this.currentPage === 1) { | |
| 231 | + this.showErrorState('加载数据失败,请重试') | |
| 232 | + } else { | |
| 233 | + this.showMessage('加载更多数据失败', 'error') | |
| 234 | + } | |
| 235 | + } finally { | |
| 236 | + this.loading = false | |
| 237 | + } | |
| 238 | + }, | |
| 239 | + | |
| 240 | + // 刷新数据 | |
| 241 | + refreshData() { | |
| 242 | + this.currentPage = 1 | |
| 243 | + this.hasMore = true | |
| 244 | + this.loadmoreStatus = 'loadmore' | |
| 245 | + this.dataList = [] | |
| 246 | + this.loadUsageList() | |
| 247 | + }, | |
| 248 | + | |
| 249 | + // 加载更多 | |
| 250 | + async loadMore() { | |
| 251 | + if (this.loading || !this.hasMore || this.loadmoreStatus === 'nomore') return | |
| 252 | + | |
| 253 | + this.loadmoreStatus = 'loading' | |
| 254 | + this.currentPage++ | |
| 255 | + await this.loadUsageList() | |
| 256 | + }, | |
| 257 | + | |
| 258 | + // 跳转到添加页面 | |
| 259 | + goToAdd() { | |
| 260 | + uni.navigateTo({ | |
| 261 | + url: '/pages/usage-form/usage-form' | |
| 262 | + }) | |
| 263 | + }, | |
| 264 | + | |
| 265 | + // 作废使用记录 | |
| 266 | + async handleCancel(item) { | |
| 267 | + if (item.isCanceled) { | |
| 268 | + uni.showToast({ | |
| 269 | + title: '该记录已作废', | |
| 270 | + icon: 'none' | |
| 271 | + }) | |
| 272 | + return | |
| 273 | + } | |
| 274 | + | |
| 275 | + // 显示确认对话框 | |
| 276 | + const confirm = await this.showConfirmDialog('确认作废', '确定要作废这条使用记录吗?作废后无法恢复。') | |
| 277 | + | |
| 278 | + if (!confirm) { | |
| 279 | + return | |
| 280 | + } | |
| 281 | + | |
| 282 | + try { | |
| 283 | + uni.showLoading({ | |
| 284 | + title: '正在作废...' | |
| 285 | + }) | |
| 286 | + | |
| 287 | + const res = await usageApi.cancelUsage(item.id, '用户手动作废') | |
| 288 | + | |
| 289 | + if (res.code === 200) { | |
| 290 | + uni.showToast({ | |
| 291 | + title: '作废成功', | |
| 292 | + icon: 'success' | |
| 293 | + }) | |
| 294 | + // 刷新列表 | |
| 295 | + this.refreshData() | |
| 296 | + } else { | |
| 297 | + throw new Error(res.message || res.msg || '作废失败') | |
| 298 | + } | |
| 299 | + } catch (error) { | |
| 300 | + console.error('作废使用记录失败:', error) | |
| 301 | + uni.showToast({ | |
| 302 | + title: error.message || error.msg || '作废失败,请重试', | |
| 303 | + icon: 'none' | |
| 304 | + }) | |
| 305 | + } finally { | |
| 306 | + uni.hideLoading() | |
| 307 | + } | |
| 308 | + }, | |
| 309 | + | |
| 310 | + // 显示确认对话框 | |
| 311 | + showConfirmDialog(title, content) { | |
| 312 | + return new Promise((resolve) => { | |
| 313 | + uni.showModal({ | |
| 314 | + title: title, | |
| 315 | + content: content, | |
| 316 | + success: (res) => { | |
| 317 | + resolve(res.confirm) | |
| 318 | + }, | |
| 319 | + fail: () => { | |
| 320 | + resolve(false) | |
| 321 | + } | |
| 322 | + }) | |
| 323 | + }) | |
| 324 | + }, | |
| 325 | + | |
| 326 | + // 格式化时间 | |
| 327 | + formatTime(timestamp) { | |
| 328 | + if (!timestamp) return '无' | |
| 329 | + const date = new Date(timestamp) | |
| 330 | + return date.toLocaleString('zh-CN', { | |
| 331 | + year: 'numeric', | |
| 332 | + month: '2-digit', | |
| 333 | + day: '2-digit', | |
| 334 | + hour: '2-digit', | |
| 335 | + minute: '2-digit' | |
| 336 | + }) | |
| 337 | + }, | |
| 338 | + | |
| 339 | + // 获取状态样式类 | |
| 340 | + getStatusClass(isCanceled) { | |
| 341 | + return isCanceled ? 'canceled' : 'normal' | |
| 342 | + }, | |
| 343 | + | |
| 344 | + // 显示加载状态 | |
| 345 | + showLoadingState() { | |
| 346 | + // 加载状态由模板控制 | |
| 347 | + }, | |
| 348 | + | |
| 349 | + // 显示错误状态 | |
| 350 | + showErrorState(message) { | |
| 351 | + uni.showToast({ | |
| 352 | + title: message, | |
| 353 | + icon: 'none' | |
| 354 | + }) | |
| 355 | + }, | |
| 356 | + | |
| 357 | + // 显示消息提示 | |
| 358 | + showMessage(message, type = 'info') { | |
| 359 | + uni.showToast({ | |
| 360 | + title: message, | |
| 361 | + icon: type === 'error' ? 'error' : 'none' | |
| 362 | + }) | |
| 363 | + }, | |
| 364 | + | |
| 365 | + // 格式化日期范围显示 | |
| 366 | + formatDateRange() { | |
| 367 | + if (!this.startDate || !this.endDate) { | |
| 368 | + return '请选择日期' | |
| 369 | + } | |
| 370 | + | |
| 371 | + // 直接格式化日期字符串,避免时区问题 | |
| 372 | + const formatDisplay = (dateStr) => { | |
| 373 | + // dateStr 格式为 YYYY-MM-DD | |
| 374 | + const parts = dateStr.split('-') | |
| 375 | + if (parts.length === 3) { | |
| 376 | + return `${parts[0]}/${parts[1]}/${parts[2]}` | |
| 377 | + } | |
| 378 | + return dateStr | |
| 379 | + } | |
| 380 | + | |
| 381 | + return `${formatDisplay(this.startDate)} - ${formatDisplay(this.endDate)}` | |
| 382 | + }, | |
| 383 | + | |
| 384 | + // 处理日期确认 | |
| 385 | + handleDateConfirm(e) { | |
| 386 | + console.log('日期选择结果:', e) | |
| 387 | + | |
| 388 | + // uni-calendar组件返回的是包含start和end的对象 | |
| 389 | + if (e && e.range && e.range.data) { | |
| 390 | + // 更新dateRange用于API调用(时间戳格式) | |
| 391 | + const startTime = new Date(e.range.data[0]).getTime() | |
| 392 | + const endTime = new Date(e.range.data[e.range.data.length-1]).getTime() | |
| 393 | + this.dateRange = [startTime, endTime] | |
| 394 | + | |
| 395 | + // 更新startDate和endDate用于显示 | |
| 396 | + this.startDate = e.range.data[0] | |
| 397 | + this.endDate = e.range.data[e.range.data.length-1] | |
| 398 | + | |
| 399 | + // 日期变化时重新加载数据 | |
| 400 | + this.refreshData() | |
| 401 | + } | |
| 402 | + } | |
| 403 | + } | |
| 404 | + } | |
| 405 | +</script> | |
| 406 | + | |
| 407 | +<style lang="scss" scoped> | |
| 408 | + .container { | |
| 409 | + min-height: 100vh; | |
| 410 | + background: linear-gradient(135deg, #e8f5e9 0%, #b2dfdb 100%); | |
| 411 | + padding: 40rpx 40rpx; | |
| 412 | + box-sizing: border-box; | |
| 413 | + } | |
| 414 | + | |
| 415 | + .date-filter-card { | |
| 416 | + background: #fff; | |
| 417 | + border-radius: 32rpx; | |
| 418 | + box-shadow: 0 8rpx 32rpx rgba(76, 175, 80, 0.1); | |
| 419 | + padding: 40rpx; | |
| 420 | + margin-bottom: 40rpx; | |
| 421 | + } | |
| 422 | + | |
| 423 | + .date-filter-header { | |
| 424 | + display: flex; | |
| 425 | + justify-content: space-between; | |
| 426 | + align-items: center; | |
| 427 | + } | |
| 428 | + | |
| 429 | + .date-range-display { | |
| 430 | + display: flex; | |
| 431 | + align-items: center; | |
| 432 | + background: #f9fff9; | |
| 433 | + border: 3rpx solid #c8e6c9; | |
| 434 | + border-radius: 24rpx; | |
| 435 | + padding: 24rpx 32rpx; | |
| 436 | + cursor: pointer; | |
| 437 | + transition: all 0.2s ease; | |
| 438 | + justify-content: space-between; | |
| 439 | + width: 100%; | |
| 440 | + box-sizing: border-box; | |
| 441 | + } | |
| 442 | + | |
| 443 | + .date-range-display:active { | |
| 444 | + border-color: #43a047; | |
| 445 | + background: #fff; | |
| 446 | + transform: scale(0.98); | |
| 447 | + } | |
| 448 | + | |
| 449 | + .date-text { | |
| 450 | + font-size: 28rpx; | |
| 451 | + color: #2e7d32; | |
| 452 | + flex: 1; | |
| 453 | + } | |
| 454 | + | |
| 455 | + .calendar-icon { | |
| 456 | + font-size: 32rpx; | |
| 457 | + margin-left: 16rpx; | |
| 458 | + } | |
| 459 | + | |
| 460 | + .add-btn-wrapper { | |
| 461 | + margin-bottom: 40rpx; | |
| 462 | + display: flex; | |
| 463 | + justify-content: flex-end; | |
| 464 | + } | |
| 465 | + | |
| 466 | + .add-btn { | |
| 467 | + display: flex; | |
| 468 | + align-items: center; | |
| 469 | + background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); | |
| 470 | + color: #fff; | |
| 471 | + padding: 24rpx 48rpx; | |
| 472 | + border-radius: 32rpx; | |
| 473 | + font-size: 28rpx; | |
| 474 | + font-weight: 600; | |
| 475 | + box-shadow: 0 8rpx 24rpx rgba(67, 233, 123, 0.3); | |
| 476 | + transition: all 0.2s ease; | |
| 477 | + } | |
| 478 | + | |
| 479 | + .add-btn:active { | |
| 480 | + transform: scale(0.95); | |
| 481 | + box-shadow: 0 4rpx 12rpx rgba(67, 233, 123, 0.4); | |
| 482 | + } | |
| 483 | + | |
| 484 | + .add-text { | |
| 485 | + letter-spacing: 2rpx; | |
| 486 | + } | |
| 487 | + | |
| 488 | + .list-card { | |
| 489 | + background: #fff; | |
| 490 | + border-radius: 32rpx; | |
| 491 | + box-shadow: 0 8rpx 32rpx rgba(76, 175, 80, 0.1); | |
| 492 | + overflow: hidden; | |
| 493 | + max-height: 60vh; | |
| 494 | + } | |
| 495 | + | |
| 496 | + .list-header { | |
| 497 | + background: linear-gradient(120deg, #43e97b 0%, #38f9d7 100%); | |
| 498 | + padding: 32rpx 40rpx; | |
| 499 | + color: #fff; | |
| 500 | + font-weight: 600; | |
| 501 | + letter-spacing: 2rpx; | |
| 502 | + display: flex; | |
| 503 | + justify-content: space-between; | |
| 504 | + align-items: center; | |
| 505 | + } | |
| 506 | + | |
| 507 | + .header-text { | |
| 508 | + font-size: 32rpx; | |
| 509 | + } | |
| 510 | + | |
| 511 | + .total-count { | |
| 512 | + font-size: 28rpx; | |
| 513 | + opacity: 0.9; | |
| 514 | + } | |
| 515 | + | |
| 516 | + .list-content { | |
| 517 | + max-height: calc(60vh - 120rpx); | |
| 518 | + overflow-y: auto; | |
| 519 | + } | |
| 520 | + | |
| 521 | + .list-item { | |
| 522 | + padding: 32rpx 40rpx; | |
| 523 | + border-bottom: 2rpx solid #f0f0f0; | |
| 524 | + cursor: pointer; | |
| 525 | + transition: all 0.2s ease; | |
| 526 | + position: relative; | |
| 527 | + } | |
| 528 | + | |
| 529 | + .list-item:active { | |
| 530 | + background: #f8fff8; | |
| 531 | + transform: translateX(8rpx); | |
| 532 | + } | |
| 533 | + | |
| 534 | + .list-item:last-child { | |
| 535 | + border-bottom: none; | |
| 536 | + } | |
| 537 | + | |
| 538 | + .item-header { | |
| 539 | + display: flex; | |
| 540 | + justify-content: space-between; | |
| 541 | + align-items: center; | |
| 542 | + margin-bottom: 16rpx; | |
| 543 | + } | |
| 544 | + | |
| 545 | + .product-name { | |
| 546 | + font-weight: 600; | |
| 547 | + color: #2e7d32; | |
| 548 | + font-size: 32rpx; | |
| 549 | + } | |
| 550 | + | |
| 551 | + .usage-time { | |
| 552 | + font-size: 24rpx; | |
| 553 | + color: #6a9c6a; | |
| 554 | + } | |
| 555 | + | |
| 556 | + .item-details { | |
| 557 | + display: grid; | |
| 558 | + grid-template-columns: 1fr 1fr; | |
| 559 | + gap: 16rpx; | |
| 560 | + margin-bottom: 16rpx; | |
| 561 | + } | |
| 562 | + | |
| 563 | + .detail-item { | |
| 564 | + display: flex; | |
| 565 | + align-items: center; | |
| 566 | + font-size: 28rpx; | |
| 567 | + color: #555; | |
| 568 | + } | |
| 569 | + | |
| 570 | + .detail-label { | |
| 571 | + color: #6a9c6a; | |
| 572 | + margin-right: 12rpx; | |
| 573 | + font-size: 24rpx; | |
| 574 | + } | |
| 575 | + | |
| 576 | + .detail-value { | |
| 577 | + color: #2e7d32; | |
| 578 | + font-weight: 500; | |
| 579 | + } | |
| 580 | + | |
| 581 | + .item-footer { | |
| 582 | + display: flex; | |
| 583 | + justify-content: space-between; | |
| 584 | + align-items: center; | |
| 585 | + margin-top: 16rpx; | |
| 586 | + } | |
| 587 | + | |
| 588 | + .status-badge { | |
| 589 | + display: inline-block; | |
| 590 | + padding: 8rpx 24rpx; | |
| 591 | + border-radius: 40rpx; | |
| 592 | + font-size: 24rpx; | |
| 593 | + font-weight: 500; | |
| 594 | + text-align: center; | |
| 595 | + } | |
| 596 | + | |
| 597 | + .status-badge.normal { | |
| 598 | + background: #e8f5e9; | |
| 599 | + color: #2e7d32; | |
| 600 | + border: 2rpx solid #c8e6c9; | |
| 601 | + } | |
| 602 | + | |
| 603 | + .status-badge.canceled { | |
| 604 | + background: #ffebee; | |
| 605 | + color: #c62828; | |
| 606 | + border: 2rpx solid #ffcdd2; | |
| 607 | + } | |
| 608 | + | |
| 609 | + .action-buttons { | |
| 610 | + display: flex; | |
| 611 | + align-items: center; | |
| 612 | + gap: 16rpx; | |
| 613 | + } | |
| 614 | + | |
| 615 | + .action-btn { | |
| 616 | + display: flex; | |
| 617 | + align-items: center; | |
| 618 | + padding: 12rpx 24rpx; | |
| 619 | + border-radius: 24rpx; | |
| 620 | + font-size: 24rpx; | |
| 621 | + font-weight: 500; | |
| 622 | + transition: all 0.2s ease; | |
| 623 | + box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); | |
| 624 | + } | |
| 625 | + | |
| 626 | + .action-btn:active { | |
| 627 | + transform: scale(0.95); | |
| 628 | + box-shadow: 0 1rpx 4rpx rgba(0, 0, 0, 0.15); | |
| 629 | + } | |
| 630 | + | |
| 631 | + .cancel-btn { | |
| 632 | + background: linear-gradient(135deg, #ef5350 0%, #e53935 100%); | |
| 633 | + color: #fff; | |
| 634 | + } | |
| 635 | + | |
| 636 | + .cancel-btn:active { | |
| 637 | + background: linear-gradient(135deg, #e53935 0%, #c62828 100%); | |
| 638 | + } | |
| 639 | + | |
| 640 | + .btn-text { | |
| 641 | + letter-spacing: 1rpx; | |
| 642 | + } | |
| 643 | + | |
| 644 | + .loading { | |
| 645 | + text-align: center; | |
| 646 | + padding: 80rpx 40rpx; | |
| 647 | + color: #6a9c6a; | |
| 648 | + font-size: 28rpx; | |
| 649 | + } | |
| 650 | + | |
| 651 | + .empty-state { | |
| 652 | + text-align: center; | |
| 653 | + padding: 120rpx 40rpx; | |
| 654 | + color: #6a9c6a; | |
| 655 | + } | |
| 656 | + | |
| 657 | + .empty-icon { | |
| 658 | + font-size: 120rpx; | |
| 659 | + margin-bottom: 32rpx; | |
| 660 | + opacity: 0.5; | |
| 661 | + } | |
| 662 | +</style> | |
| 663 | + | ... | ... |