feat: 生产管理

This commit is contained in:
Czw996 2022-04-09 21:30:08 +08:00
parent 1f3c76e9f2
commit b62456d9c1
5 changed files with 296 additions and 15 deletions

View file

@ -46,10 +46,14 @@ class ProductionOrderSerializer(BaseSerializer):
return super().validate(attrs)
def create(self, validated_data):
validated_data['remain_quantity'] = validated_data['total_quantity']
validated_data['quantity_produced'] = 0
validated_data['creator'] = self.user
return super().create(validated_data)
def save(self, **kwargs):
kwargs['remain_quantity'] = kwargs['total_quantity']
return super().save(**kwargs)
class ProductionRecordSerializer(BaseSerializer):
"""生产记录"""

View file

@ -0,0 +1,109 @@
<template>
<div>
<a-select
v-model="value"
:placeholder="placeholder"
allowClear
show-search
:disabled="disabled"
:filter-option="false"
:not-found-content="loading ? undefined : '暂无数据'"
style="width: 100%;"
@search="search"
@change="change"
@focus="focus"
@popupScroll="scroll"
>
<div slot="notFoundContent" style="text-align: center;">
<a-spin size="small" :spinning="loading" />
</div>
<a-select-option v-for="item in options" :key="item.id" :value="item.id">
{{ item.name }}
</a-select-option>
<a-select-option v-if="!isloadedAll" key="loading" value="loading" disabled>
<div style="text-align: center; height: 24px;">
<a-spin size="small" :spinning="loading" />
</div>
</a-select-option>
</a-select>
</div>
</template>
<script>
import { goodsOption } from "@/api/option";
export default {
props: ["value", "placeholder", "disabled", "defaultItem"],
model: { prop: "value", event: "change" },
data() {
return {
items: [],
searchForm: { search: "", page: 1, is_active: true, page_size: 15 },
totalRows: 0,
loading: false,
timeout: null,
};
},
computed: {
options() {
let items = [...this.items];
if (this.defaultItem && !this.loading && (!this.searchForm.search || this.searchForm.search == "")) {
if (this.defaultItem.goods && this.defaultItem.goods_name) {
if (this.items.findIndex((item) => item.id == this.value) == -1) {
items.splice(0, 0, { id: this.defaultItem, name: this.defaultItem.goods_name });
}
}
}
return items;
},
isloadedAll() {
return this.searchForm.page * this.searchForm.page_size >= this.totalRows;
},
},
methods: {
list() {
if (this.searchForm.page === 1) {
this.items = [];
}
this.loading = true;
goodsOption(this.searchForm)
.then((data) => {
this.totalRows = data.count;
this.items.push(...data.results);
})
.finally(() => {
this.loading = false;
});
},
change(value) {
this.$emit("change", value);
},
focus() {
this.searchForm.page = 1;
this.list();
},
scroll({ target }) {
if (!this.loading && target.scrollTop + target.offsetHeight === target.scrollHeight) {
if (!this.isloadedAll) {
this.searchForm.page += 1;
this.list();
}
}
},
search(value) {
this.searchForm.page = 1;
this.searchForm.search = value;
if (this.timeout) {
clearTimeout(this.timeout);
this.timeout = null;
}
this.timeout = setTimeout(this.list, 300);
},
},
};
</script>
<style scoped></style>

View file

@ -0,0 +1,113 @@
<template>
<div>
<a-select
v-model="value"
:placeholder="placeholder"
allowClear
show-search
:disabled="disabled"
:filter-option="false"
:not-found-content="loading ? undefined : '暂无数据'"
style="width: 100%;"
@search="search"
@change="change"
@focus="focus"
@popupScroll="scroll"
>
<div slot="notFoundContent" style="text-align: center;">
<a-spin size="small" :spinning="loading" />
</div>
<a-select-option v-for="item in options" :key="item.id" :value="item.id">
{{ item.number }}
</a-select-option>
<a-select-option v-if="!isloadedAll" key="loading" value="loading" disabled>
<div style="text-align: center; height: 24px;">
<a-spin size="small" :spinning="loading" />
</div>
</a-select-option>
</a-select>
</div>
</template>
<script>
import { saleOrdersOption } from "@/api/option";
export default {
props: ["value", "placeholder", "disabled", "defaultItem"],
model: { prop: "value", event: "change" },
data() {
return {
items: [],
searchForm: { search: "", page: 1, is_active: true, page_size: 15 },
totalRows: 0,
loading: false,
timeout: null,
};
},
computed: {
options() {
let items = [...this.items];
if (this.defaultItem && !this.loading && (!this.searchForm.search || this.searchForm.search == "")) {
if (this.defaultItem.sales_order && this.defaultItem.sales_order_number) {
if (this.items.findIndex((item) => item.id == this.value) == -1) {
items.splice(0, 0, { id: this.defaultItem, name: this.defaultItem.sales_order_number });
}
}
}
return items;
},
isloadedAll() {
return this.searchForm.page * this.searchForm.page_size >= this.totalRows;
},
},
methods: {
list() {
if (this.searchForm.page === 1) {
this.items = [];
}
this.loading = true;
saleOrdersOption(this.searchForm)
.then((data) => {
this.totalRows = data.count;
this.items.push(...data.results);
})
.finally(() => {
this.loading = false;
});
},
change(value) {
this.$emit("change", value);
let index = this.items.findIndex((item) => item.id == value);
if (index != -1) {
this.$emit("select", this.items[index]);
}
},
focus() {
this.searchForm.page = 1;
this.list();
},
scroll({ target }) {
if (!this.loading && target.scrollTop + target.offsetHeight === target.scrollHeight) {
if (!this.isloadedAll) {
this.searchForm.page += 1;
this.list();
}
}
},
search(value) {
this.searchForm.page = 1;
this.searchForm.search = value;
if (this.timeout) {
clearTimeout(this.timeout);
this.timeout = null;
}
this.timeout = setTimeout(this.list, 300);
},
},
};
</script>
<style scoped></style>

View file

@ -3,15 +3,53 @@
<a-modal v-model="visible" :confirmLoading="loading" :maskClosable="false" @cancel="cancel" @ok="confirm">
<div slot="title">{{ form.id ? "编辑生产计划" : "新增生产计划" }}</div>
<div>
<a-form-model ref="form" :model="form" :rules="rules" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }">
<a-form-model ref="form" :model="dataForm" :rules="rules" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }">
<a-form-model-item prop="number" label="生产单号">
<a-input v-model="dataForm.number" />
</a-form-model-item>
<a-form-model-item prop="is_related" label="类型">
<a-radio-group v-model="form.is_related" button-style="solid">
<a-radio-group v-model="dataForm.is_related" button-style="solid" @change="switchType">
<a-radio-button :value="false">按库存生产</a-radio-button>
<a-radio-button :value="true">按订单生产</a-radio-button>
</a-radio-group>
</a-form-model-item>
<a-form-model-item v-if="!dataForm.is_related" prop="goods" label="产品">
<goods-select v-model="dataForm.goods" />
</a-form-model-item>
<a-form-model-item v-if="dataForm.is_related" prop="sales_order" label="销售单">
<sales-order-select
v-model="dataForm.sales_order"
@select="(item) => (goodsItems = item.sales_goods_items)"
/>
</a-form-model-item>
<a-form-model-item v-if="dataForm.is_related" prop="goods" label="产品">
<a-select v-model="dataForm.goods" style="width: 100%;">
<a-select-option v-for="item in goodsItems" :key="item.goods" :value="item.goods">
{{ item.goods_name }}
</a-select-option>
</a-select>
</a-form-model-item>
<a-form-model-item prop="total_quantity" label="生产数量">
<a-input-number v-model="dataForm.total_quantity" style="width: 100%;" />
</a-form-model-item>
<a-form-model-item prop="start_time" label="开始时间">
<a-date-picker
v-model="dataForm.start_time"
placeholder="请选择时间"
valueFormat="YYYY-MM-DD HH:mm:ss"
show-time
style="width: 100%;"
/>
</a-form-model-item>
<a-form-model-item prop="end_time" label="结束时间">
<a-date-picker
v-model="dataForm.end_time"
placeholder="请选择时间"
valueFormat="YYYY-MM-DD HH:mm:ss"
show-time
style="width: 100%;"
/>
</a-form-model-item>
</a-form-model>
</div>
</a-modal>
@ -19,22 +57,23 @@
</template>
<script>
// import { clientCreate, clientUpdate } from "@/api/basicData";
import { productionOrderCreate, productionOrderUpdate, productionOrderNumber } from "@/api/production";
export default {
name: "FormModal",
components: {
GoodsSelect: () => import("@/components/GoodsSelect/index"),
SalesOrderSelect: () => import("@/components/SalesOrderSelect/index"),
},
props: ["visible", "form"],
model: { prop: "visible", event: "cancel" },
data() {
return {
rules: {
name: [{ required: true, message: "请输入名称", trigger: "change" }],
number: [{ required: true, message: "请输入编号", trigger: "change" }],
initial_arrears_amount: [
{ pattern: new RegExp(/^\d{0,14}(?:\.\d{0,2})?$/), message: "初期欠款金额格式不正确", trigger: "change" },
],
},
loading: false,
dataForm: {},
goodsItems: [],
};
},
methods: {
@ -42,8 +81,8 @@ export default {
this.$refs.form.validate((valid) => {
if (valid) {
this.loading = true;
let func = this.form.id ? clientUpdate : clientCreate;
func(this.form)
let func = this.dataForm.id ? productionOrderUpdate : productionOrderCreate;
func(this.dataForm)
.then((data) => {
this.$message.success(this.form.id ? "修改成功" : "新增成功");
this.$emit(this.form.id ? "update" : "create", data);
@ -59,6 +98,23 @@ export default {
this.$emit("cancel", false);
this.$refs.form.resetFields();
},
switchType() {
this.dataForm = { ...this.dataForm, sales_order: undefined, goods: undefined };
},
},
watch: {
visible(value) {
if (value) {
if (this.form.id) {
this.dataForm = { ...this.form };
} else {
this.dataForm = { is_related: false };
productionOrderNumber().then((data) => {
this.dataForm = { ...this.dataForm, number: data.number };
});
}
}
},
},
};
</script>

View file

@ -13,7 +13,7 @@
</a-col>
<div style="margin-bottom: 12px; float: right">
<a-button type="primary" icon="plus" style="margin: 0 8px" @click="openCreateModal(defaultForm)">
<a-button type="primary" icon="plus" style="margin: 0 8px" @click="openCreateModal({})">
新增生产计划
</a-button>
</div>
@ -128,7 +128,6 @@ export default {
],
visible: false,
targetItem: {},
defaultForm: { is_related: false },
};
},
methods: {