form-dialog.vue 10.6 KB
<template>
	<el-dialog
		:title="isEdit ? '编辑店内支出' : '添加店内支出'"
		:visible.sync="visible"
		:close-on-click-modal="false"
		class="NCC-dialog NCC-dialog_center"
		width="900px"
		@close="handleClose"
	>
		<div class="form-content" v-loading="loading">
			<el-form ref="form" :model="dataForm" :rules="rules" label-width="120px" label-position="right">
				<el-form-item label="门店" prop="storeId">
					<el-select v-model="dataForm.storeId" placeholder="请选择门店" clearable filterable style="width: 100%" @change="handleStoreChange">
						<el-option v-for="store in storeOptions" :key="store.id" :label="store.dm" :value="store.id" />
					</el-select>
				</el-form-item>
				<el-form-item v-show="false" label="门店名称" prop="storeName">
					<el-input v-model="dataForm.storeName" placeholder="自动填充" readonly />
				</el-form-item>
				<el-form-item label="支出分类" prop="expenseCategoryId">
					<el-select v-model="dataForm.expenseCategoryId" placeholder="请选择支出分类" clearable filterable style="width: 100%" @change="handleCategoryChange">
						<el-option v-for="category in categoryOptions" :key="category.id" :label="category.typeName" :value="category.id" />
					</el-select>
				</el-form-item>
				<el-form-item v-show="false" label="支出分类名称" prop="expenseCategoryName">
					<el-input v-model="dataForm.expenseCategoryName" placeholder="自动填充" readonly />
				</el-form-item>
				<el-form-item label="支出日期" prop="expenseDate">
					<el-date-picker
						v-model="dataForm.expenseDate"
						type="date"
						placeholder="请选择支出日期"
						format="yyyy-MM-dd"
						value-format="timestamp"
						style="width: 100%"
					/>
				</el-form-item>
				<el-row :gutter="20">
					<el-col :span="8">
						<el-form-item label="单价" prop="unitPrice">
							<el-input-number v-model="dataForm.unitPrice" placeholder="请输入单价" :precision="2" :min="0" :step="1" style="width: 100%" @change="calculateAmount" />
						</el-form-item>
					</el-col>
					<el-col :span="8">
						<el-form-item label="数量" prop="quantity">
							<el-input-number v-model="dataForm.quantity" placeholder="请输入数量" :min="0" :step="1" style="width: 100%" @change="calculateAmount" />
						</el-form-item>
					</el-col>
					<el-col :span="8">
						<el-form-item label="金额" prop="amount">
							<el-input-number v-model="dataForm.amount" placeholder="自动计算" :precision="2" :min="0" :step="1" style="width: 100%" :disabled="true" />
						</el-form-item>
					</el-col>
				</el-row>
				<el-form-item label="备注" prop="memo">
					<el-input v-model="dataForm.memo" type="textarea" :rows="3" placeholder="请输入备注说明" maxlength="1000" show-word-limit />
				</el-form-item>
				<el-form-item label="附件" prop="attachment">
					<el-upload
						:action="uploadUrl"
						:headers="uploadHeaders"
						:file-list="fileList"
						:on-success="handleUploadSuccess"
						:on-remove="handleRemove"
						:before-upload="beforeUpload"
						multiple
						:limit="9"
					>
						<el-button size="small" icon="el-icon-upload">选择文件</el-button>
						<div slot="tip" class="el-upload__tip">支持多文件上传,最多9个文件,单个文件不超过10MB</div>
					</el-upload>
				</el-form-item>
			</el-form>
		</div>
		<div slot="footer" class="dialog-footer">
			<el-button @click="handleClose">取消</el-button>
			<el-button type="primary" @click="dataFormSubmit" :loading="submitLoading">确定</el-button>
		</div>
	</el-dialog>
</template>

<script>
import request from '@/utils/request'

export default {
	data() {
		return {
			visible: false,
			loading: false,
			submitLoading: false,
			isEdit: false,
			dataForm: {
				id: '',
				storeId: '',
				storeName: '',
				expenseCategoryId: '',
				expenseCategoryName: '',
				expenseDate: '',
				unitPrice: 0,
				quantity: 0,
				amount: 0,
				memo: '',
				attachment: [],
				relatedReimbursementId: '',
				relatedPurchaseRecordId: ''
			},
			rules: {
				storeId: [
					{ required: true, message: '请选择门店', trigger: 'change' }
				],
				expenseDate: [
					{ required: true, message: '请选择支出日期', trigger: 'change' }
				],
				amount: [
					{ required: true, message: '金额不能为空', trigger: 'blur' },
					{ type: 'number', min: 0, message: '金额必须大于等于0', trigger: 'blur' }
				]
			},
			storeOptions: [],
			categoryOptions: [],
			fileList: [],
			uploadUrl: '',
			uploadHeaders: {}
		}
	},
	created() {
		this.initStoreOptions()
		this.initCategoryOptions()
		this.initUploadConfig()
	},
	methods: {
		init(id) {
			this.visible = true
			this.isEdit = !!id
			this.loading = false
			this.submitLoading = false
			this.resetForm()
			if (id) {
				this.loadData(id)
			}
		},
		resetForm() {
			this.dataForm = {
				id: '',
				storeId: '',
				storeName: '',
				expenseCategoryId: '',
				expenseCategoryName: '',
				expenseDate: '',
				unitPrice: 0,
				quantity: 0,
				amount: 0,
				memo: '',
				attachment: [],
				relatedReimbursementId: '',
				relatedPurchaseRecordId: ''
			}
			this.fileList = []
			if (this.$refs.form) {
				this.$refs.form.clearValidate()
			}
		},
		loadData(id) {
			this.loading = true
			request({
				url: `/api/Extend/LqStoreExpense/${id}`,
				method: 'GET'
			}).then(res => {
				if (res.code == 200 && res.data) {
					this.dataForm = {
						id: res.data.id,
						storeId: res.data.storeId,
						storeName: res.data.storeName,
						expenseCategoryId: res.data.expenseCategoryId || '',
						expenseCategoryName: res.data.expenseCategoryName || '',
						expenseDate: res.data.expenseDate ? new Date(res.data.expenseDate).getTime() : '',
						unitPrice: res.data.unitPrice || 0,
						quantity: res.data.quantity || 0,
						amount: res.data.amount || 0,
						memo: res.data.memo || '',
						attachment: res.data.attachment || [],
						relatedReimbursementId: res.data.relatedReimbursementId || '',
						relatedPurchaseRecordId: res.data.relatedPurchaseRecordId || ''
					}
					// 处理附件列表
					if (this.dataForm.attachment && this.dataForm.attachment.length > 0) {
						this.fileList = this.dataForm.attachment.map(item => ({
							name: item.name,
							url: item.url,
							fileId: item.fileId,
							uid: item.fileId || Date.now() + Math.random()
						}))
					} else {
						this.fileList = []
					}
				}
				this.loading = false
			}).catch(() => {
				this.loading = false
			})
		},
		initStoreOptions() {
			request({
				url: '/api/Extend/LqMdxx',
				method: 'GET',
				data: {
					currentPage: 1,
					pageSize: 1000
				}
			}).then(res => {
				if (res.data && res.data.list) {
					this.storeOptions = res.data.list
				}
			}).catch(() => {
				this.storeOptions = []
			})
		},
		initCategoryOptions() {
			request({
				url: '/api/Extend/LqReimbursementCategory',
				method: 'GET',
				data: {
					currentPage: 1,
					pageSize: 1000
				}
			}).then(res => {
				if (res.data && res.data.list) {
					this.categoryOptions = res.data.list
				}
			}).catch(() => {
				this.categoryOptions = []
			})
		},
		initUploadConfig() {
			this.uploadUrl = this.define.comUploadUrl
			this.uploadHeaders = {
				Authorization: this.$store.getters.token
			}
		},
		handleStoreChange(value) {
			const store = this.storeOptions.find(s => s.id === value)
			if (store) {
				this.dataForm.storeName = store.dm
			} else {
				this.dataForm.storeName = ''
			}
		},
		handleCategoryChange(value) {
			const category = this.categoryOptions.find(c => c.id === value)
			if (category) {
				this.dataForm.expenseCategoryName = category.typeName
			} else {
				this.dataForm.expenseCategoryName = ''
			}
		},
		calculateAmount() {
			const unitPrice = Number(this.dataForm.unitPrice) || 0
			const quantity = Number(this.dataForm.quantity) || 0
			this.dataForm.amount = Number((unitPrice * quantity).toFixed(2))
		},
		handleUploadSuccess(res, file, fileList) {
			if (res.code == 200) {
				const fileItem = {
					name: file.name,
					fileId: res.data.name,
					url: res.data.url || res.data.name
				}
				if (!this.dataForm.attachment) {
					this.dataForm.attachment = []
				}
				this.dataForm.attachment.push(fileItem)
				this.fileList = fileList
			} else {
				this.$message.error(res.msg || '文件上传失败')
				this.fileList = fileList.filter(f => f.uid !== file.uid)
			}
		},
		handleRemove(file, fileList) {
			this.fileList = fileList
			if (this.dataForm.attachment) {
				this.dataForm.attachment = this.dataForm.attachment.filter(item => item.fileId !== file.fileId)
			}
		},
		beforeUpload(file) {
			const isLt10M = file.size / 1024 / 1024 < 10
			if (!isLt10M) {
				this.$message.error('上传文件大小不能超过 10MB!')
			}
			return isLt10M
		},
		dataFormSubmit() {
			this.$refs.form.validate((valid) => {
				if (valid) {
					// 验证金额计算
					if (this.dataForm.unitPrice && this.dataForm.quantity) {
						const calculatedAmount = (parseFloat(this.dataForm.unitPrice) || 0) * (parseFloat(this.dataForm.quantity) || 0)
						const inputAmount = parseFloat(this.dataForm.amount) || 0
						if (Math.abs(calculatedAmount - inputAmount) > 0.01) {
							this.$message.warning('金额必须等于单价乘以数量')
							return
						}
					}
					this.submitLoading = true
					const submitData = {
						storeId: this.dataForm.storeId,
						storeName: this.dataForm.storeName,
						expenseCategoryId: this.dataForm.expenseCategoryId || '',
						expenseCategoryName: this.dataForm.expenseCategoryName || '',
						expenseDate: this.dataForm.expenseDate ? new Date(this.dataForm.expenseDate).toISOString() : '',
						unitPrice: parseFloat(this.dataForm.unitPrice) || 0,
						quantity: parseInt(this.dataForm.quantity) || 0,
						amount: parseFloat(this.dataForm.amount) || 0,
						memo: this.dataForm.memo || '',
						attachment: this.dataForm.attachment || [],
						relatedReimbursementId: this.dataForm.relatedReimbursementId || '',
						relatedPurchaseRecordId: this.dataForm.relatedPurchaseRecordId || ''
					}
					const url = this.isEdit
						? `/api/Extend/LqStoreExpense/${this.dataForm.id}`
						: '/api/Extend/LqStoreExpense'
					const method = this.isEdit ? 'PUT' : 'POST'
					request({
						url,
						method,
						data: submitData
					}).then(res => {
						this.$message({
							type: 'success',
							message: res.msg || (this.isEdit ? '更新成功' : '创建成功'),
							onClose: () => {
								this.handleClose()
								this.$emit('refresh')
							}
						})
					}).catch(() => {
						this.submitLoading = false
					})
				}
			})
		},
		handleClose() {
			this.visible = false
			this.resetForm()
		}
	}
}
</script>

<style lang="scss" scoped>
.dialog-footer {
	text-align: right;
}
</style>