feat: 完成 SN 溯源、销售管理和维修记录。
This commit is contained in:
@@ -40,6 +40,7 @@
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.2",
|
||||
"@icon-park/vue-next": "^1.4.2",
|
||||
"@remixicon/vue": "^4.9.0",
|
||||
"@vueuse/core": "^13.7.0",
|
||||
"axios": "^1.11.0",
|
||||
"decimal.js": "^10.6.0",
|
||||
|
||||
20
pnpm-lock.yaml
generated
20
pnpm-lock.yaml
generated
@@ -14,6 +14,9 @@ importers:
|
||||
'@icon-park/vue-next':
|
||||
specifier: ^1.4.2
|
||||
version: 1.4.2(vue@3.5.18(typescript@5.9.2))
|
||||
'@remixicon/vue':
|
||||
specifier: ^4.9.0
|
||||
version: 4.9.0(vue@3.5.18(typescript@5.9.2))
|
||||
'@vueuse/core':
|
||||
specifier: ^13.7.0
|
||||
version: 13.7.0(vue@3.5.18(typescript@5.9.2))
|
||||
@@ -44,6 +47,9 @@ importers:
|
||||
qs:
|
||||
specifier: ^6.14.0
|
||||
version: 6.14.0
|
||||
remixicon:
|
||||
specifier: ^4.9.1
|
||||
version: 4.9.1
|
||||
sortablejs:
|
||||
specifier: ^1.15.6
|
||||
version: 1.15.6
|
||||
@@ -913,6 +919,11 @@ packages:
|
||||
'@polka/url@1.0.0-next.29':
|
||||
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
|
||||
|
||||
'@remixicon/vue@4.9.0':
|
||||
resolution: {integrity: sha512-4P0MY5ScL+MRLnHu1iT3xHAsliBwOGDIgSgHCZhSj2wkLa5NzqiQ4SGD/04VoHM+BRr8+xjmF7ZemkOUlMDJ3A==}
|
||||
peerDependencies:
|
||||
vue: '>= 3'
|
||||
|
||||
'@rolldown/pluginutils@1.0.0-beta.26':
|
||||
resolution: {integrity: sha512-r/5po89voz/QRPDmoErL10+hVuTAuz1SHvokx+yWBlOIPB5C41jC7QhLqq9kaebx/+EHyoV3z22/qBfX81Ns8A==}
|
||||
|
||||
@@ -4249,6 +4260,9 @@ packages:
|
||||
resolution: {integrity: sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
remixicon@4.9.1:
|
||||
resolution: {integrity: sha512-36gLSoujkabnCFZFDyP17VNh9piuBA/rsXUb4auSJWLGsHVXtmxLj/EM5FjaEAGnk8oIAj1Azob/DZ2N+90lAQ==}
|
||||
|
||||
repeat-element@1.1.4:
|
||||
resolution: {integrity: sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -6040,6 +6054,10 @@ snapshots:
|
||||
|
||||
'@polka/url@1.0.0-next.29': {}
|
||||
|
||||
'@remixicon/vue@4.9.0(vue@3.5.18(typescript@5.9.2))':
|
||||
dependencies:
|
||||
vue: 3.5.18(typescript@5.9.2)
|
||||
|
||||
'@rolldown/pluginutils@1.0.0-beta.26': {}
|
||||
|
||||
'@rolldown/pluginutils@1.0.0-beta.29': {}
|
||||
@@ -9657,6 +9675,8 @@ snapshots:
|
||||
es-errors: 1.3.0
|
||||
set-function-name: 2.0.2
|
||||
|
||||
remixicon@4.9.1: {}
|
||||
|
||||
repeat-element@1.1.4: {}
|
||||
|
||||
repeat-string@1.6.1: {}
|
||||
|
||||
@@ -320,6 +320,7 @@ export default {
|
||||
customerName: "客户名称",
|
||||
customerId: "客户",
|
||||
partNumber: "物料编号",
|
||||
productSpecs: "物料型号",
|
||||
saleCount: "销售数量",
|
||||
price: "单价",
|
||||
totalPrice: "总价",
|
||||
@@ -333,7 +334,7 @@ export default {
|
||||
serialNum: "序列号",
|
||||
productSn: "SN 号",
|
||||
repairMark: "返修记录",
|
||||
outProductDate: "出货时间",
|
||||
outProductDate: "出货日期",
|
||||
productType: "型号",
|
||||
mac: "MAC 地址",
|
||||
alNum: "算法标志",
|
||||
@@ -354,6 +355,11 @@ export default {
|
||||
manufacturingDate: "生产日期",
|
||||
mark: "备注",
|
||||
keyAccountId: "客户",
|
||||
repairCount: "维修次数",
|
||||
serialNum: "序列号",
|
||||
softVersion: "软件版本",
|
||||
alVersion: "算法版本",
|
||||
createDate: "创建日期",
|
||||
},
|
||||
},
|
||||
systemset: {
|
||||
@@ -456,6 +462,7 @@ export default {
|
||||
reject_success: "反审成功",
|
||||
reject_fail: "反审失败",
|
||||
reject_cannel: "反审取消",
|
||||
all_customers: "所有客户",
|
||||
},
|
||||
warehouse: {
|
||||
warehouse_item: {
|
||||
@@ -680,15 +687,6 @@ export default {
|
||||
input_productSn: "请输入SN号",
|
||||
input_mac: "请输入MAC地址",
|
||||
},
|
||||
repairrecord: {
|
||||
input_productSn: "请输入SN号",
|
||||
input_mac: "请输入MAC地址",
|
||||
input_repairStatus: "请选择维修状态",
|
||||
input_repairDate: "请输入维修日期",
|
||||
input_manufacturingDate: "请输入生产日期",
|
||||
input_mark: "请输入备注",
|
||||
input_repairMark: "请输入返修记录",
|
||||
},
|
||||
},
|
||||
purchase: {
|
||||
purchase_plan: {
|
||||
@@ -780,6 +778,24 @@ export default {
|
||||
import_fail: "导入失败",
|
||||
delete_message: "删除销售订单",
|
||||
},
|
||||
repairrecord: {
|
||||
input_productSn: "请输入SN号",
|
||||
input_mac: "请输入MAC地址",
|
||||
input_repairStatus: "请选择维修状态",
|
||||
input_repairDate: "请输入维修日期",
|
||||
input_manufacturingDate: "请输入生产日期",
|
||||
input_mark: "请输入备注",
|
||||
input_repairMark: "请输入返修记录",
|
||||
input_repair_mark: "请输入返修记录",
|
||||
input_identifier: "SN号、MAC地址、序列号至少填写一个",
|
||||
delete_message: "删除维修记录",
|
||||
identifier_tip: "提示:SN号、MAC地址、序列号三者至少填写一个",
|
||||
input_productSn_optional: "SN号(选填)",
|
||||
input_mac_optional: "MAC地址(选填)",
|
||||
input_serialNum_optional: "序列号(选填)",
|
||||
input_softVersion_optional: "软件版本(选填)",
|
||||
input_alVersion_optional: "算法版本(选填)",
|
||||
},
|
||||
},
|
||||
systemset: {
|
||||
vendor: {
|
||||
@@ -915,6 +931,13 @@ export default {
|
||||
saleorder: {
|
||||
add: "添加销售订单",
|
||||
edit: "编辑销售订单",
|
||||
showItem: "销售明细",
|
||||
baseTitle: "销售订单基本信息",
|
||||
tableTitle: "销售明细",
|
||||
},
|
||||
repairrecord: {
|
||||
add: "添加维修记录",
|
||||
edit: "编辑维修记录",
|
||||
},
|
||||
},
|
||||
systemset: {
|
||||
|
||||
@@ -7,6 +7,7 @@ import { useStatus } from "@/common/languages/mapping/base-info-mapping";
|
||||
const props = defineProps({
|
||||
tableEl: ref,
|
||||
statusLabelMapping: Function,
|
||||
tagTypeMapping: Function,
|
||||
statusParamName: {
|
||||
type: String,
|
||||
default: "status",
|
||||
@@ -37,14 +38,26 @@ const getLabel = (code: number | null) => {
|
||||
if (props.statusLabelMapping === undefined) return getCommonStatusLabel(code);
|
||||
return props.statusLabelMapping(code);
|
||||
};
|
||||
|
||||
const getTagType = (code: number | null): string => {
|
||||
if (props.tagTypeMapping !== undefined) {
|
||||
return props.tagTypeMapping(code);
|
||||
}
|
||||
// 默认逻辑:switchOnValue 为 success,其他为 info
|
||||
return code === props.switchOnValue ? "success" : "info";
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<el-table-column :label="$t('_prop.common.status')" :prop="statusParamName">
|
||||
<template #default="scope">
|
||||
<!-- 没有权限按钮时,显示带框标签 -->
|
||||
<span v-if="buttonList.length === 0">
|
||||
<el-tag :type="getTagType(scope.row[statusParamName])" size="small">
|
||||
{{ getLabel(scope.row[statusParamName]) }}
|
||||
</el-tag>
|
||||
</span>
|
||||
<template v-for="button in buttonList" :key="button.buttonName">
|
||||
<!-- 有权限按钮时,显示开关或标签 -->
|
||||
<template v-else v-for="button in buttonList" :key="button.buttonName">
|
||||
<StatusSwitch
|
||||
v-model="scope.row[statusParamName]"
|
||||
@change="val => change(val, scope.row)"
|
||||
@@ -54,6 +67,21 @@ const getLabel = (code: number | null) => {
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
<!-- 有权限但没有匹配到开关时,显示带框标签 -->
|
||||
<el-tag
|
||||
v-if="
|
||||
buttonList.length > 0 &&
|
||||
!buttonList.some(
|
||||
button =>
|
||||
(button.eventName === 'enable' && scope.row[statusParamName] === switchOffValue) ||
|
||||
(button.eventName === 'disable' && scope.row[statusParamName] === switchOnValue)
|
||||
)
|
||||
"
|
||||
:type="getTagType(scope.row[statusParamName])"
|
||||
size="small"
|
||||
>
|
||||
{{ getLabel(scope.row[statusParamName]) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
|
||||
@@ -83,7 +83,7 @@ export const dynamicRoutes: RouterConfigRaw[] = [
|
||||
component: "/systemset/vendor/form",
|
||||
meta: {
|
||||
title: "总表",
|
||||
icon: Compass,
|
||||
icon: "ep:Compass",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,18 +1,25 @@
|
||||
<script lang="ts" setup>
|
||||
import TreeSidePageableTable from "@/components/base/treeside-pageable-table/TreeSidePageableTable.vue";
|
||||
import { $t } from "@/common/languages";
|
||||
import { ref } from "vue";
|
||||
|
||||
/**
|
||||
* 必须要的变量
|
||||
*/
|
||||
|
||||
const getPageUrl = "/sale/device/getDevicePage";
|
||||
const treeSideUrl = "/sale/device/getKeyAccount";
|
||||
const tableRef = ref<InstanceType<typeof TreeSidePageableTable> | null>(null);
|
||||
const getPageUrl = "/sale/device/getDeviceSnPage";
|
||||
const treeSideUrl = "/sys/keyaccount/getKeyAccountTree";
|
||||
const searchers = [
|
||||
{
|
||||
name: "searchCode",
|
||||
type: "text" as const,
|
||||
placeholder: $t("_prop.sale.device.searchCode"),
|
||||
placeholder:
|
||||
$t("_prop.sale.devicesn.productSn") +
|
||||
"/" +
|
||||
$t("_prop.sale.devicesn.mac") +
|
||||
"/" +
|
||||
$t("_prop.sale.devicesn.serialNum"),
|
||||
},
|
||||
];
|
||||
</script>
|
||||
@@ -22,21 +29,19 @@ const searchers = [
|
||||
:searchers="searchers"
|
||||
ref="tableRef"
|
||||
:tree-side-url="treeSideUrl"
|
||||
tree-side-node-name="keyAccountId"
|
||||
tree-side-node-name="id"
|
||||
tree-side-param-name="keyAccountId"
|
||||
tree-side-title="所有客户"
|
||||
:tree-side-title="$t('_message.common.all_customers')"
|
||||
>
|
||||
<template #columns>
|
||||
<el-table-column :label="$t('_prop.sale.device.productType')" prop="productType" />
|
||||
<el-table-column :label="$t('_prop.sale.device.productSn')" prop="productSn" />
|
||||
<el-table-column :label="$t('_prop.sale.device.mac')" prop="mac" />
|
||||
<el-table-column :label="$t('_prop.sale.device.serialNum')" prop="serialNum" />
|
||||
<el-table-column :label="$t('_prop.sale.device.softVersion')" prop="softVersion" />
|
||||
<el-table-column :label="$t('_prop.sale.device.alVersion')" prop="alVersion" />
|
||||
<el-table-column :label="$t('_prop.sale.device.alNum')" prop="alNum" />
|
||||
<el-table-column :label="$t('_prop.sale.device.outStatus')" prop="outStatus" />
|
||||
<el-table-column :label="$t('_prop.sale.device.outProductDate')" prop="outProductDate" />
|
||||
<el-table-column :label="$t('_prop.sale.device.repairMark')" prop="repairMark" />
|
||||
<el-table-column :label="$t('_prop.sale.devicesn.productType')" prop="productType" />
|
||||
<el-table-column :label="$t('_prop.sale.devicesn.productSn')" prop="productSn" />
|
||||
<el-table-column :label="$t('_prop.sale.devicesn.mac')" prop="mac" />
|
||||
<el-table-column :label="$t('_prop.sale.devicesn.serialNum')" prop="serialNum" />
|
||||
<el-table-column :label="$t('_prop.sale.devicesn.softVersion')" prop="softVersion" />
|
||||
<el-table-column :label="$t('_prop.sale.devicesn.AlVersion')" prop="alVersion" />
|
||||
<el-table-column :label="$t('_prop.sale.devicesn.outProductDate')" prop="outProductDate" />
|
||||
<el-table-column :label="$t('_prop.sale.devicesn.repairMark')" prop="repairRecords" show-overflow-tooltip />
|
||||
</template>
|
||||
</TreeSidePageableTable>
|
||||
</template>
|
||||
|
||||
@@ -6,26 +6,64 @@ import BaseForm from "@/components/base/base-form/BaseForm.vue";
|
||||
import { $t } from "@/common/languages";
|
||||
import type { FormInstance, FormRules } from "element-plus";
|
||||
import TreeSidePageableTable from "@/components/base/treeside-pageable-table/TreeSidePageableTable.vue";
|
||||
import { get } from "@/common/http/request";
|
||||
|
||||
/**
|
||||
* 必须要的变量
|
||||
*/
|
||||
|
||||
const getPageUrl = "/sale/repairrecord/getRepairRecordPage";
|
||||
const getPageUrl = "/sale/repairrecord/getRepairReportPage";
|
||||
const addUrl = "/sale/repairrecord/addRepairRecord";
|
||||
const editUrl = "/sale/repairrecord/updateRepairRecord";
|
||||
const removeUrl = "/sale/repairrecord/deleteRepairRecord";
|
||||
const treeSideUrl = "/sale/repairrecord/getKeyAccount";
|
||||
const treeSideUrl = "/sys/keyaccount/getKeyAccountTree";
|
||||
const keyAccountSelectUrl = "/sys/keyaccount/getKeyAccountSelectList";
|
||||
const searchers = [
|
||||
{ name: "searchCode", type: "text" as const, placeholder: $t("_prop.sale.repairrecord.searchCode") },
|
||||
{
|
||||
name: "searchCode",
|
||||
type: "text" as const,
|
||||
placeholder: $t("_prop.sale.repairrecord.productSn") + "/" + $t("_prop.sale.repairrecord.mac"),
|
||||
clearable: true,
|
||||
},
|
||||
];
|
||||
const rules = reactive<FormRules>({});
|
||||
|
||||
const validateIdentifier = (_rule: any, _value: any, callback: any) => {
|
||||
const formData = form.value;
|
||||
if (formData.productSn || formData.mac || formData.serialNum) {
|
||||
callback();
|
||||
} else {
|
||||
callback(new Error($t("_message.sale.repairrecord.input_identifier")));
|
||||
}
|
||||
};
|
||||
|
||||
const rules = reactive<FormRules>({
|
||||
repairMark: [{ required: true, message: $t("_message.sale.repairrecord.input_repair_mark"), trigger: "blur" }],
|
||||
productSn: [{ validator: validateIdentifier, trigger: "blur" }],
|
||||
});
|
||||
|
||||
const keyAccountOptions = ref<{ label: string; value: string }[]>([]);
|
||||
|
||||
const loadKeyAccountOptions = async () => {
|
||||
const res = await get(keyAccountSelectUrl);
|
||||
if (res.data) {
|
||||
keyAccountOptions.value = res.data.map((item: { label: string; value: string }) => ({
|
||||
label: item.label,
|
||||
value: item.value,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadKeyAccountOptions();
|
||||
});
|
||||
|
||||
/**
|
||||
* 基本不变通用变量
|
||||
*/
|
||||
const tableRef = ref<InstanceType<typeof TreeSidePageableTable> | null>(null);
|
||||
const { useAdd, useEdit, useRemove, useGeneralPageRef } = usePage(tableRef);
|
||||
const { title, visible, formType, form } = useGeneralPageRef();
|
||||
|
||||
/**
|
||||
* 可以自定义的变量
|
||||
*/
|
||||
@@ -36,25 +74,32 @@ const add = () => {
|
||||
visible.value = true;
|
||||
formType.value = false;
|
||||
};
|
||||
|
||||
const edit = (row: any) => {
|
||||
title.value = "_title.sale.repairrecord.edit";
|
||||
form.value = { ...row };
|
||||
visible.value = true;
|
||||
formType.value = true;
|
||||
};
|
||||
|
||||
const remove = (row: any) => {
|
||||
useRemove(removeUrl, row.id, "_message.sale.repairrecord.delete_message");
|
||||
};
|
||||
const submit = (form: any, formRef: FormInstance | undefined) => {
|
||||
|
||||
const submit = (formData: any, formRef: FormInstance | undefined) => {
|
||||
if (formRef !== undefined) {
|
||||
formRef.validate(valid => {
|
||||
if (valid) {
|
||||
if (formType.value) useEdit(editUrl, form, visible);
|
||||
else useAdd(addUrl, form, visible);
|
||||
if (formType.value) {
|
||||
useEdit(editUrl, formData, visible);
|
||||
} else {
|
||||
useAdd(addUrl, formData, visible);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const topButtonClick = (eventName: string) => {
|
||||
switch (eventName) {
|
||||
case "add":
|
||||
@@ -62,6 +107,7 @@ const topButtonClick = (eventName: string) => {
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const operateButtonClick = (eventName: string, row: any) => {
|
||||
switch (eventName) {
|
||||
case "edit":
|
||||
@@ -79,27 +125,98 @@ const operateButtonClick = (eventName: string, row: any) => {
|
||||
:searchers="searchers"
|
||||
ref="tableRef"
|
||||
:tree-side-url="treeSideUrl"
|
||||
tree-side-node-name="keyAccountId"
|
||||
tree-side-param-name="keyAccountId"
|
||||
tree-side-title="所有客户"
|
||||
tree-side-node-name="id"
|
||||
tree-side-param-name="keyAccountIdSearch"
|
||||
:tree-side-title="$t('_message.common.all_customers')"
|
||||
>
|
||||
<template #tool-button>
|
||||
<DefaultToolButton @top-button-click="topButtonClick" />
|
||||
</template>
|
||||
<template #columns>
|
||||
<el-table-column prop="id" type="hidden" width="40" />
|
||||
<el-table-column :label="$t('_prop.sale.repairrecord.productType')" prop="productType" />
|
||||
<el-table-column :label="$t('_prop.sale.repairrecord.productSn')" prop="productSn" />
|
||||
<el-table-column :label="$t('_prop.sale.repairrecord.mac')" prop="mac" />
|
||||
<el-table-column :label="$t('_prop.sale.repairrecord.outProductDate')" prop="outProductDate" />
|
||||
<el-table-column :label="$t('_prop.sale.repairrecord.repairDate')" prop="repairDate" />
|
||||
<el-table-column :label="$t('_prop.sale.repairrecord.repairMark')" prop="repairMark" />
|
||||
<el-table-column :label="$t('_prop.common.createDate')" prop="createDate" />
|
||||
<el-table-column :label="$t('_prop.sale.repairrecord.repairCount')" prop="repairCount" />
|
||||
<el-table-column :label="$t('_prop.sale.repairrecord.repairMark')" prop="repairRecords" show-overflow-tooltip />
|
||||
<DefaultOperateButtonColumn @operate-button-click="operateButtonClick" />
|
||||
</template>
|
||||
</TreeSidePageableTable>
|
||||
<BaseForm v-model:visible="visible" @submit="submit" v-model:form="form" :title="$t(title)" :rules="rules">
|
||||
<template #form-items>
|
||||
<el-form-item prop="id" v-if="false"><el-input v-model="form.id" /></el-form-item>
|
||||
<el-alert
|
||||
:title="$t('_message.sale.repairrecord.identifier_tip')"
|
||||
type="info"
|
||||
:closable="false"
|
||||
style="margin-bottom: 15px"
|
||||
/>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="$t('_prop.sale.repairrecord.productType')" prop="productType">
|
||||
<el-input v-model="form.productType" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="$t('_prop.sale.repairrecord.productSn')" prop="productSn">
|
||||
<el-input
|
||||
v-model="form.productSn"
|
||||
:placeholder="$t('_message.sale.repairrecord.input_productSn_optional')"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="$t('_prop.sale.repairrecord.mac')" prop="mac">
|
||||
<el-input v-model="form.mac" :placeholder="$t('_message.sale.repairrecord.input_mac_optional')" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="$t('_prop.sale.devicesn.serialNum')" prop="serialNum">
|
||||
<el-input
|
||||
v-model="form.serialNum"
|
||||
:placeholder="$t('_message.sale.repairrecord.input_serialNum_optional')"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="$t('_prop.sale.devicesn.softVersion')" prop="softVersion">
|
||||
<el-input
|
||||
v-model="form.softVersion"
|
||||
:placeholder="$t('_message.sale.repairrecord.input_softVersion_optional')"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="$t('_prop.sale.devicesn.AlVersion')" prop="alVersion">
|
||||
<el-input
|
||||
v-model="form.alVersion"
|
||||
:placeholder="$t('_message.sale.repairrecord.input_alVersion_optional')"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="$t('_prop.sale.repairrecord.keyAccountId')" prop="keyAccountId">
|
||||
<el-select v-model="form.keyAccountId" clearable style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in keyAccountOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="Number(item.value)"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item :label="$t('_prop.sale.repairrecord.repairMark')" prop="repairMark">
|
||||
<el-input v-model="form.repairMark" type="textarea" :rows="3" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</BaseForm>
|
||||
</template>
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
<script lang="ts" setup>
|
||||
import BasePageableTable from "@/components/base/base-pageable-table/BasePageableTable.vue";
|
||||
import DefaultToolButton from "@/components/base/default-tool-button/DefaultToolButton.vue";
|
||||
import DefaultOperateButtonColumn from "@/components/base/default-column/DefaultOperateButtonColumn.vue";
|
||||
import DefaultStatusSwitchColumn from "@/components/base/default-column/DefaultStatusSwitchColumn.vue";
|
||||
import { usePage } from "@/composables/use-page";
|
||||
import BaseForm from "@/components/base/base-form/BaseForm.vue";
|
||||
import { $t } from "@/common/languages";
|
||||
import type { FormInstance, FormRules } from "element-plus";
|
||||
import { formatDate } from "@/common/utils/format-utils";
|
||||
import { ElMessage, type FormInstance, type FormRules } from "element-plus";
|
||||
import BaseFormWithTable from "@/components/base/base-form-with-table/BaseFormWithTable.vue";
|
||||
import BaseItemDialog from "@/components/base/base-item-dialog/BaseItemDialog.vue";
|
||||
import ExpandablePageableTable from "@/components/base/expandable-pageable-table/ExpandablePageableTable.vue";
|
||||
import { get } from "@/common/http/request";
|
||||
import { type FieldMappingConfig } from "@/components/base/base-form-with-table/type";
|
||||
import { useStatus } from "@/common/languages/mapping/base-info-mapping";
|
||||
|
||||
/**
|
||||
* 必须要的变量
|
||||
@@ -16,45 +21,152 @@ const getPageUrl = "/sale/saleorder/getSaleOrderPage";
|
||||
const addUrl = "/sale/saleorder/addSaleOrder";
|
||||
const editUrl = "/sale/saleorder/updateSaleOrder";
|
||||
const removeUrl = "/sale/saleorder/deleteSaleOrder";
|
||||
const approveUrl = "/sale/saleorder/approveSaleOrder";
|
||||
const rejectUrl = "/sale/saleorder/unapproveSaleOrder";
|
||||
const itemUrl = "/sale/saleorder/getSaleOrderItemList";
|
||||
const keyAccountSelectUrl = "/sys/keyaccount/getKeyAccountSelectList";
|
||||
|
||||
const searchers = [
|
||||
{ name: "customerName", type: "text" as const, placeholder: $t("_prop.sale.saleorder.customerName") },
|
||||
{
|
||||
name: "customerName",
|
||||
type: "text" as const,
|
||||
placeholder: $t("_prop.sale.saleorder.customerName"),
|
||||
clearable: true,
|
||||
},
|
||||
];
|
||||
const rules = reactive<FormRules>({});
|
||||
|
||||
const rules = reactive<FormRules>({
|
||||
formCode: [{ required: true, message: $t("_message.sale.saleorder.input_formCode"), trigger: "blur" }],
|
||||
formName: [{ required: true, message: $t("_message.sale.saleorder.input_formName"), trigger: "blur" }],
|
||||
customerId: [{ required: true, message: $t("_message.sale.saleorder.select_customerId"), trigger: "change" }],
|
||||
customerName: [{ required: true, message: $t("_message.sale.saleorder.select_customerId"), trigger: "change" }],
|
||||
saleOrderItems: [
|
||||
{
|
||||
required: true,
|
||||
validator: (rule, value, callback) => {
|
||||
if (value === undefined || value.length === 0) {
|
||||
callback($t("_message.sale.saleorder.no_sale_order_items"));
|
||||
}
|
||||
callback();
|
||||
},
|
||||
trigger: "change",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const itemArrayName = "saleOrderItems";
|
||||
const getDetailUrl = "/sale/saleorder/getSaleOrderItemList";
|
||||
|
||||
/**
|
||||
* 基本不变通用变量
|
||||
*/
|
||||
const tableRef = ref<InstanceType<typeof BasePageableTable> | null>(null);
|
||||
const { useAdd, useEdit, useRemove, useGeneralPageRef } = usePage(tableRef);
|
||||
const { title, visible, formType, form } = useGeneralPageRef();
|
||||
const tableRef = ref<InstanceType<typeof ExpandablePageableTable> | null>(null);
|
||||
const { useAdd, useEdit, useRemove, useGeneralPageRef, useApprove, useReject } = usePage(tableRef);
|
||||
const { title, visible, formType, form, itemVisible, itemParentId } = useGeneralPageRef();
|
||||
const { getFormStatusLabel } = useStatus();
|
||||
const baseFormWithTableRef = ref<InstanceType<typeof BaseFormWithTable>>();
|
||||
|
||||
/**
|
||||
* 可以自定义的变量
|
||||
*/
|
||||
|
||||
const keyAccountOptions = ref<{ label: string; value: string }[]>([]);
|
||||
|
||||
const loadKeyAccountOptions = async () => {
|
||||
const res = await get(keyAccountSelectUrl);
|
||||
if (res.data) {
|
||||
keyAccountOptions.value = res.data.map((item: { label: string; value: string }) => ({
|
||||
label: item.label,
|
||||
value: item.value,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadKeyAccountOptions();
|
||||
});
|
||||
|
||||
// 生成单据编号
|
||||
const generateFormCode = () => {
|
||||
const now = new Date();
|
||||
const dateStr =
|
||||
now.getFullYear().toString() +
|
||||
String(now.getMonth() + 1).padStart(2, "0") +
|
||||
String(now.getDate()).padStart(2, "0") +
|
||||
String(now.getHours()).padStart(2, "0") +
|
||||
String(now.getMinutes()).padStart(2, "0") +
|
||||
String(now.getSeconds()).padStart(2, "0");
|
||||
return "SO" + dateStr;
|
||||
};
|
||||
|
||||
// 监听客户选择变化,同步更新客户名称
|
||||
const handleCustomerChange = (value: number) => {
|
||||
const selected = keyAccountOptions.value.find(item => Number(item.value) === value);
|
||||
if (selected) {
|
||||
form.value.customerName = selected.label;
|
||||
}
|
||||
};
|
||||
|
||||
const add = () => {
|
||||
form.value = {};
|
||||
form.value = {
|
||||
formCode: generateFormCode(),
|
||||
};
|
||||
title.value = "_title.sale.saleorder.add";
|
||||
visible.value = true;
|
||||
formType.value = false;
|
||||
};
|
||||
|
||||
const edit = (row: any) => {
|
||||
title.value = "_title.sale.saleorder.edit";
|
||||
form.value = { ...row };
|
||||
form.value = {
|
||||
...row,
|
||||
saleOrderItems: [],
|
||||
};
|
||||
visible.value = true;
|
||||
formType.value = true;
|
||||
|
||||
// 加载明细数据
|
||||
get(getDetailUrl, { saleOrderId: row.id })
|
||||
.then(res => {
|
||||
form.value.saleOrderItems = res.data || [];
|
||||
})
|
||||
.catch(err => {
|
||||
console.error("Failed to load sale order items:", err);
|
||||
});
|
||||
};
|
||||
|
||||
const remove = (row: any) => {
|
||||
useRemove(removeUrl, row.id, "_message.sale.saleorder.delete_message");
|
||||
};
|
||||
const submit = (form: any, formRef: FormInstance | undefined) => {
|
||||
if (formRef !== undefined) {
|
||||
formRef.validate(valid => {
|
||||
|
||||
const approve = (row: any) => {
|
||||
useApprove(approveUrl, row.id);
|
||||
};
|
||||
|
||||
const reject = (row: any) => {
|
||||
useReject(rejectUrl, row.id);
|
||||
};
|
||||
|
||||
const showItem = (row: any) => {
|
||||
itemParentId.value = row.id;
|
||||
itemVisible.value = true;
|
||||
};
|
||||
|
||||
const submit = (formData: any, formRef: FormInstance | undefined) => {
|
||||
const targetRef = formRef || (baseFormWithTableRef.value as any)?.baseFormRef;
|
||||
if (!targetRef) return;
|
||||
|
||||
targetRef.validate((valid: any) => {
|
||||
if (valid) {
|
||||
if (formType.value) useEdit(editUrl, form, visible);
|
||||
else useAdd(addUrl, form, visible);
|
||||
if (formType.value) {
|
||||
useEdit(editUrl, formData, visible);
|
||||
} else {
|
||||
useAdd(addUrl, formData, visible);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const topButtonClick = (eventName: string) => {
|
||||
switch (eventName) {
|
||||
case "add":
|
||||
@@ -62,6 +174,7 @@ const topButtonClick = (eventName: string) => {
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const operateButtonClick = (eventName: string, row: any) => {
|
||||
switch (eventName) {
|
||||
case "edit":
|
||||
@@ -70,29 +183,210 @@ const operateButtonClick = (eventName: string, row: any) => {
|
||||
case "remove":
|
||||
remove(row);
|
||||
break;
|
||||
case "approve":
|
||||
approve(row);
|
||||
break;
|
||||
case "reject":
|
||||
reject(row);
|
||||
break;
|
||||
case "showItem":
|
||||
showItem(row);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const authShowFunc = (row: any, button: globalThis.ButtonProp) => {
|
||||
if (row.formStatus === 1 && button.eventName === "approve") return false;
|
||||
if (row.formStatus === 0 && button.eventName === "reject") return false;
|
||||
if (row.formStatus === 1 && button.eventName === "edit") return false;
|
||||
if (row.formStatus === 1 && button.eventName === "remove") return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
const mappingConfig: FieldMappingConfig = {
|
||||
partNumber: {
|
||||
sourceKey: "物料编号",
|
||||
defaultValue: "",
|
||||
},
|
||||
productSpecs: {
|
||||
sourceKey: "物料型号",
|
||||
defaultValue: "",
|
||||
},
|
||||
saleCount: {
|
||||
sourceKey: "数量",
|
||||
defaultValue: 0,
|
||||
transform: (val: any) => {
|
||||
const num = Number(val);
|
||||
return isNaN(num) ? 0 : num;
|
||||
},
|
||||
},
|
||||
price: {
|
||||
sourceKey: "单价",
|
||||
defaultValue: 0,
|
||||
transform: (val: any) => {
|
||||
const num = Number(val);
|
||||
return isNaN(num) ? 0 : num;
|
||||
},
|
||||
},
|
||||
saleMark: {
|
||||
sourceKey: "备注",
|
||||
defaultValue: "",
|
||||
},
|
||||
};
|
||||
|
||||
// 计算总价
|
||||
const calculateTotalPrice = (row: any) => {
|
||||
if (row.saleCount && row.price) {
|
||||
row.totalPrice = Number((row.saleCount * row.price).toFixed(2));
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<BasePageableTable :url="getPageUrl" :searchers="searchers" ref="tableRef">
|
||||
<ExpandablePageableTable
|
||||
:url="getPageUrl"
|
||||
:searchers="searchers"
|
||||
ref="tableRef"
|
||||
:item-url="itemUrl"
|
||||
item-id-key="id"
|
||||
item-id-name="saleOrderId"
|
||||
item-field-name="saleOrderItems"
|
||||
>
|
||||
<template #tool-button>
|
||||
<DefaultToolButton @top-button-click="topButtonClick" />
|
||||
</template>
|
||||
<template #columns>
|
||||
<el-table-column prop="id" type="hidden" width="40" />
|
||||
<el-table-column :label="$t('_prop.sale.saleorder.formCode')" prop="formCode" />
|
||||
<el-table-column :label="$t('_prop.sale.saleorder.customerName')" prop="customerName" />
|
||||
<el-table-column :label="$t('_prop.sale.saleorder.totalValue')" prop="totalValue" />
|
||||
<el-table-column :label="$t('_prop.sale.saleorder.formName')" prop="formName" />
|
||||
<el-table-column :label="$t('_prop.sale.saleorder.formMark')" prop="formMark" />
|
||||
<el-table-column :label="$t('_prop.sale.saleorder.formMark')" prop="formMark" show-overflow-tooltip />
|
||||
<el-table-column :label="$t('_prop.common.createDate')" prop="createDate" :formatter="formatDate" />
|
||||
<el-table-column :label="$t('_prop.sale.saleorder.formStatus')" prop="formStatus" />
|
||||
<DefaultOperateButtonColumn @operate-button-click="operateButtonClick" />
|
||||
<DefaultStatusSwitchColumn
|
||||
status-param-name="formStatus"
|
||||
:status-label-mapping="getFormStatusLabel"
|
||||
:switch-on-value="1"
|
||||
:switch-off-value="0"
|
||||
/>
|
||||
<DefaultOperateButtonColumn @operate-button-click="operateButtonClick" :auth-show-func="authShowFunc" />
|
||||
</template>
|
||||
</BasePageableTable>
|
||||
<BaseForm v-model:visible="visible" @submit="submit" v-model:form="form" :title="$t(title)" :rules="rules">
|
||||
<template #item-content="{ itemData }">
|
||||
<el-table :data="itemData" size="small" border stripe>
|
||||
<el-table-column :label="$t('_prop.sale.saleorder.partNumber')" prop="partNumber" width="150" />
|
||||
<el-table-column :label="$t('_prop.sale.saleorder.productSpecs')" prop="productSpecs" width="200" />
|
||||
<el-table-column :label="$t('_prop.sale.saleorder.saleCount')" prop="saleCount" />
|
||||
<el-table-column :label="$t('_prop.sale.saleorder.price')" prop="price" />
|
||||
<el-table-column :label="$t('_prop.sale.saleorder.totalPrice')" prop="totalPrice" />
|
||||
</el-table>
|
||||
</template>
|
||||
</ExpandablePageableTable>
|
||||
<BaseFormWithTable
|
||||
ref="baseFormWithTableRef"
|
||||
v-model:visible="visible"
|
||||
@submit="submit"
|
||||
v-model:form="form"
|
||||
:title="$t(title)"
|
||||
:rules="rules"
|
||||
:base-title="$t('_title.sale.saleorder.baseTitle')"
|
||||
:table-title="$t('_title.sale.saleorder.tableTitle')"
|
||||
item-array-name="saleOrderItems"
|
||||
upload-desc="销售明细"
|
||||
:mapping-config="mappingConfig"
|
||||
>
|
||||
<template #form-items>
|
||||
<el-form-item prop="id" v-if="false"><el-input v-model="form.id" /></el-form-item>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item :label="$t('_prop.sale.saleorder.formCode')" prop="formCode">
|
||||
<el-input v-model="form.formCode" disabled />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item :label="$t('_prop.sale.saleorder.customerId')" prop="customerId">
|
||||
<el-select v-model="form.customerId" clearable style="width: 100%" @change="handleCustomerChange">
|
||||
<el-option
|
||||
v-for="item in keyAccountOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="Number(item.value)"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item :label="$t('_prop.sale.saleorder.formName')" prop="formName">
|
||||
<el-input v-model="form.formName" :placeholder="$t('_message.sale.saleorder.input_formName')" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item :label="$t('_prop.sale.saleorder.formMark')" prop="formMark">
|
||||
<el-input
|
||||
v-model="form.formMark"
|
||||
:placeholder="$t('_message.sale.saleorder.input_formMark')"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</BaseForm>
|
||||
<template #form-table-columns>
|
||||
<el-table-column :label="$t('_prop.sale.saleorder.partNumber')" width="150">
|
||||
<template #default="{ row, $index }">
|
||||
<el-form-item :prop="`${itemArrayName}.${$index}.partNumber`" :rules="rules.partNumber">
|
||||
<el-input v-model="row.partNumber" size="small" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="$t('_prop.sale.saleorder.saleCount')" width="120">
|
||||
<template #default="{ row, $index }">
|
||||
<el-form-item :prop="`${itemArrayName}.${$index}.saleCount`">
|
||||
<el-input-number v-model="row.saleCount" size="small" :min="1" @change="calculateTotalPrice(row)" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="$t('_prop.sale.saleorder.price')" width="120">
|
||||
<template #default="{ row, $index }">
|
||||
<el-form-item :prop="`${itemArrayName}.${$index}.price`">
|
||||
<el-input-number
|
||||
v-model="row.price"
|
||||
size="small"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
@change="calculateTotalPrice(row)"
|
||||
/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="$t('_prop.sale.saleorder.totalPrice')" width="120">
|
||||
<template #default="{ row }">
|
||||
<span>{{ row.totalPrice?.toFixed(2) || "0.00" }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="$t('_prop.sale.saleorder.saleMark')" width="150">
|
||||
<template #default="{ row, $index }">
|
||||
<el-form-item :prop="`${itemArrayName}.${$index}.saleMark`">
|
||||
<el-input v-model="row.saleMark" placeholder="请输入备注" size="small" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
</BaseFormWithTable>
|
||||
<BaseItemDialog
|
||||
:title="$t('_title.sale.saleorder.showItem')"
|
||||
v-model:visible="itemVisible"
|
||||
:url="getDetailUrl"
|
||||
parent-param-name="saleOrderId"
|
||||
v-model:parent-param-value="itemParentId"
|
||||
>
|
||||
<template #columns>
|
||||
<el-table-column :label="$t('_prop.sale.saleorder.partNumber')" prop="partNumber" width="150" />
|
||||
<el-table-column :label="$t('_prop.sale.saleorder.productSpecs')" prop="productSpecs" width="200" />
|
||||
<el-table-column :label="$t('_prop.sale.saleorder.saleCount')" prop="saleCount" />
|
||||
<el-table-column :label="$t('_prop.sale.saleorder.price')" prop="price" />
|
||||
<el-table-column :label="$t('_prop.sale.saleorder.totalPrice')" prop="totalPrice" />
|
||||
<el-table-column :label="$t('_prop.sale.saleorder.saleMark')" prop="saleMark" />
|
||||
</template>
|
||||
</BaseItemDialog>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user