fix: 第一版错误修改前的存档。

This commit is contained in:
c
2026-03-16 14:17:03 +08:00
parent 48cd47dd72
commit 0c4e4679b3
32 changed files with 1123 additions and 288 deletions

BIN
public/favicon (2).png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 20 KiB

BIN
public/logo1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -18,7 +18,7 @@ import {
export const defaultServiceConfig: ServiceConfig = {
layout: {
name: "Teek Design Vue3",
name: "牛安管理系统",
avatar: "/avatar.png",
titleMode: TitleModeEnum.ProjectPage,
layoutMode: LayoutModeEnum.Vertical,
@@ -36,15 +36,15 @@ export const defaultServiceConfig: ServiceConfig = {
env: [],
},
themePanelTriggerPosition: ThemePanelTriggerPositionEnum.Header,
globalAlert: {
enabled: false,
text: "Teek Design Vue3 祝您圣诞快乐,愿节日的欢乐与祝福如雪花般纷至沓来!",
startDate: "2025-12-25",
endDate: "2025-12-25",
type: "primary",
closable: true,
showIcon: false,
},
// globalAlert: {
// enabled: false,
// text: "Teek Design Vue3 祝您圣诞快乐,愿节日的欢乐与祝福如雪花般纷至沓来!",
// startDate: "2025-12-25",
// endDate: "2025-12-25",
// type: "primary",
// closable: true,
// showIcon: false,
// },
},
theme: {
// 默认与 css var 一致,在这里配置一份,方便生成 1 - 9 的基础色

File diff suppressed because it is too large Load Diff

View File

@@ -898,6 +898,7 @@ export default {
input_iconName: "请输入图标名称",
input_sort: "请输入排序值",
input_parentId: "请输入父级权限ID0表示根节点",
select_parentId: "请选择父级权限",
delete_message: "删除权限",
},
},
@@ -948,6 +949,7 @@ export default {
},
production_issue: {
returnDialog: "退料编辑",
showItem: "发料明细",
},
finishedproductreceipt: {
add: "添加成品入库单",

View File

@@ -1,5 +1,5 @@
<script lang="ts" setup>
import { Upload } from "@icon-park/vue-next";
import { Upload, Download } from "@icon-park/vue-next";
import BaseForm from "../base-form/BaseForm.vue";
import { ElMessage, type UploadInstance, type UploadUserFile } from "element-plus";
import * as XLSX from "xlsx";
@@ -15,6 +15,7 @@ const props = defineProps({
uploadDesc: String,
itemArrayName: { type: String, required: true },
mappingConfig: { type: Object, required: true },
templateFileName: { type: String, default: "模板" },
});
const emit = defineEmits<Emits>();
const form = defineModel<any>("form");
@@ -104,16 +105,13 @@ const handleFileChange = (file: any) => {
const maxSize = 10 * 1024 * 1024; // 10MB
if (file.size > maxSize) {
ElMessage.error("文件大小不能超过 10MB");
clearAllData();
return;
}
clearAllData();
// 更新当前文件显示
currentFile.value = file;
// 解析 Excel
// 解析 Excel(追加模式,不清空原有数据)
parseExcel(file.raw);
};
const parseExcel = (file: any) => {
@@ -149,11 +147,15 @@ const parseExcel = (file: any) => {
form.value[props.itemArrayName] = [];
}
// 使用 splice 替换所有内容,这是最安全的触发数组更新的方式
form.value[props.itemArrayName].splice(0, form.value[props.itemArrayName].length, ...mappedData);
// 追加数据到现有数组
form.value[props.itemArrayName].push(...mappedData);
}
ElMessage.success(`解析成功,共 ${mappedData.length}物料数据`);
ElMessage.success(`解析成功,共追加 ${mappedData.length} 条数据`);
// 清空上传组件的文件列表,允许用户再次选择文件进行追加
uploadRef.value?.clearFiles();
currentFile.value = null;
} catch (error) {
console.error(error);
ElMessage.error("文件解析失败,请检查文件格式");
@@ -165,32 +167,36 @@ const parseExcel = (file: any) => {
// 【新增】存储当前上传的文件信息,用于显示和移除
const currentFile = ref<UploadUserFile | null>(null);
// 【新增】深度清理函数
const resetFormData = () => {
// 1. 清空表单验证
// 【新增】只清理表格数据(保留基本表单数据)
const clearTableData = () => {
// 1. 清空表格数据
if (form.value && Array.isArray(form.value[props.itemArrayName])) {
form.value[props.itemArrayName].splice(0, form.value[props.itemArrayName].length);
}
// 2. 清空表单验证(仅表格相关)
if (baseFormComponentRef.value) {
// 方式 A: 调用暴露的 clearValidate 方法
baseFormComponentRef.value.clearValidate();
// 方式 B: 或者访问内部 formRef (如果暴露了)
// baseFormComponentRef.value.formRef?.clearValidate();
}
// 2. 重置 form 对象 (保留非表格字段?通常重置是全部清空或设为初始值,这里设为空对象)
if (form.value) {
// 只清空表格数据,保留其他表单字段?还是全部重置?
// 根据需求“数据清理不干净”,通常指表格文件
// 这里我们清空表格数组和文件,其他字段由 BaseForm 的 reset 逻辑处理或手动置空
if (Array.isArray(form.value[props.itemArrayName])) {
form.value[props.itemArrayName].splice(0, form.value[props.itemArrayName].length);
}
ElMessage.success("表格数据已清空");
};
// 【新增】深度清理函数(清理表格+文件,保留基本表单数据)
const resetFormData = () => {
// 1. 清空表格数据
if (form.value && Array.isArray(form.value[props.itemArrayName])) {
form.value[props.itemArrayName].splice(0, form.value[props.itemArrayName].length);
}
// 3. 清空文件状态
// 2. 清空文件状态
currentFile.value = null;
uploadRef.value?.clearFiles();
// 4. 如果需要完全重置 form 为初始空对象,取消下面注释
form.value = {};
// 3. 清空表单验证
if (baseFormComponentRef.value) {
baseFormComponentRef.value.clearValidate();
}
};
// 【新增】增加一行
@@ -220,6 +226,43 @@ const removeFile = () => {
clearAllData();
ElMessage.success("文件已移除,表格数据已清空");
};
// 【新增】下载模板文件
const downloadTemplate = () => {
// 1. 根据 mappingConfig 生成表头
const headers: string[] = [];
for (const [targetKey, config] of Object.entries(props.mappingConfig)) {
const fieldConfig = config as ExcelDataMapping;
// 使用 sourceKey 作为表头,如果是数组则使用第一个
let header: string;
if (Array.isArray(fieldConfig.sourceKey)) {
header = fieldConfig.sourceKey[0];
} else {
header = fieldConfig.sourceKey;
}
headers.push(header);
}
// 2. 创建工作簿
const wb = XLSX.utils.book_new();
// 3. 创建工作表(只有表头,没有数据行)
const ws = XLSX.utils.aoa_to_sheet([headers]);
// 4. 设置列宽
const colWidths = headers.map(() => ({ wch: 20 }));
ws["!cols"] = colWidths;
// 5. 将工作表添加到工作簿
XLSX.utils.book_append_sheet(wb, ws, "模板");
// 6. 生成文件并下载
const fileName = `${props.templateFileName}.xlsx`;
XLSX.writeFile(wb, fileName);
ElMessage.success("模板文件下载成功");
};
// 监听 BaseForm 发出的 reset 事件
const handleBaseFormReset = () => {
resetFormData();
@@ -229,6 +272,8 @@ defineExpose({
resetFormData,
addRow,
deleteRow,
clearTableData,
downloadTemplate,
});
</script>
<template>
@@ -266,6 +311,10 @@ defineExpose({
<span class="tip-desc" v-if="props.uploadDesc">{{ uploadDesc }}</span>
</span>
</el-upload>
<el-button type="success" @click="downloadTemplate">
<el-icon><Download /></el-icon>
下载模板文件
</el-button>
</div>
</el-form-item>
@@ -288,7 +337,7 @@ defineExpose({
<el-icon><Plus /></el-icon>
增加一行
</el-button>
<el-button type="warning" link @click="resetFormData">
<el-button type="warning" link @click="clearTableData">
<el-icon><Delete /></el-icon>
清空表格
</el-button>

View File

@@ -0,0 +1,155 @@
<script lang="ts" setup>
import { get } from "@/common/http/request";
interface TreeNode {
id: number | string;
label: string;
children?: TreeNode[];
}
const props = defineProps({
url: String,
data: Array as () => TreeNode[],
placeholder: String,
nodeKey: { type: String, default: "id" },
labelKey: { type: String, default: "label" },
childrenKey: { type: String, default: "children" },
clearable: { type: Boolean, default: true },
disabled: { type: Boolean, default: false },
});
const modelValue = defineModel<number | string | undefined>({ required: true });
const treeData = ref<TreeNode[]>([]);
const treeSelectRef = ref();
const popoverVisible = ref(false);
const selectedLabel = ref("");
const defaultProps = {
children: props.childrenKey,
label: props.labelKey,
};
const loadData = async () => {
if (props.url) {
const rawData = await get(props.url).then(res => res.data);
treeData.value = rawData || [];
} else if (props.data) {
treeData.value = props.data;
}
};
const findNodeLabel = (nodes: TreeNode[], targetId: number | string): string => {
for (const node of nodes) {
if (node[props.nodeKey as keyof TreeNode] === targetId) {
return node[props.labelKey as keyof TreeNode] as string;
}
if (node.children && node.children.length > 0) {
const label = findNodeLabel(node.children, targetId);
if (label) return label;
}
}
return "";
};
const handleNodeClick = (node: TreeNode) => {
modelValue.value = node[props.nodeKey as keyof TreeNode] as number | string;
selectedLabel.value = node[props.labelKey as keyof TreeNode] as string;
popoverVisible.value = false;
};
const handleClear = () => {
modelValue.value = undefined;
selectedLabel.value = "";
};
watch(
() => modelValue.value,
newVal => {
if (newVal !== undefined && newVal !== null) {
selectedLabel.value = findNodeLabel(treeData.value, newVal);
} else {
selectedLabel.value = "";
}
},
{ immediate: true }
);
watch(
() => treeData.value,
() => {
if (modelValue.value !== undefined && modelValue.value !== null) {
selectedLabel.value = findNodeLabel(treeData.value, modelValue.value);
}
}
);
onMounted(loadData);
defineExpose({
reload: loadData,
});
</script>
<template>
<el-popover
ref="treeSelectRef"
v-model:visible="popoverVisible"
placement="bottom-start"
:width="200"
trigger="click"
:disabled="disabled"
popper-class="tree-select-popover"
>
<template #reference>
<el-input
v-model="selectedLabel"
readonly
:placeholder="placeholder"
:disabled="disabled"
:clearable="clearable"
@clear="handleClear"
class="tree-select-input"
>
<template #suffix>
<el-icon class="el-input__icon">
<arrow-down :class="{ 'is-reverse': popoverVisible }" />
</el-icon>
</template>
</el-input>
</template>
<div class="tree-select-dropdown">
<el-tree
:data="treeData"
:props="defaultProps"
:node-key="nodeKey"
default-expand-all
highlight-current
:current-node-key="modelValue"
@node-click="handleNodeClick"
/>
</div>
</el-popover>
</template>
<style scoped>
.tree-select-input {
width: 100%;
}
.tree-select-input :deep(.el-input__inner) {
cursor: pointer;
}
.tree-select-input :deep(.el-input__icon) {
transition: transform 0.3s;
}
.tree-select-input :deep(.el-input__icon.is-reverse) {
transform: rotate(180deg);
}
.tree-select-dropdown {
max-height: 300px;
overflow-y: auto;
}
</style>

View File

@@ -23,6 +23,13 @@ const loadData = async () => {
if (props.data !== undefined && treeData.value === undefined) {
treeData.value = props.data;
}
// 树数据加载完成后,如果有选中的值,使用 setCheckedKeys 设置选中状态
// 因为 default-checked-keys 只在组件初始化时生效
nextTick(() => {
if (treeRef.value && treeCheck.value !== undefined && treeCheck.value.length > 0) {
treeRef.value.setCheckedKeys(treeCheck.value);
}
});
};
const clearSelect = () => {
@@ -35,7 +42,8 @@ const defaultProps = {
};
const checkHandle = (checkedNode: any, treeStatus: any) => {
treeCheck.value = treeStatus.checkedKeys;
// 将字符串类型的 id 转换为数字类型
treeCheck.value = treeStatus.checkedKeys.map((id: string | number) => (typeof id === "string" ? Number(id) : id));
};
const clickHandle = (clickNode: any, nodeAttr: any, treeNode: any, event: any) => {
@@ -43,11 +51,13 @@ const clickHandle = (clickNode: any, nodeAttr: any, treeNode: any, event: any) =
};
const initSelect = (newV: number[]) => {
treeRef.value?.setCheckedKeys(newV);
if (treeRef.value && newV !== undefined && newV.length > 0) {
treeRef.value.setCheckedKeys(newV);
}
};
watch(treeCheck, (newV: number[] | undefined) => {
if (newV === undefined) return;
if (newV === undefined || newV.length === 0) return;
initSelect(newV);
});
@@ -64,11 +74,12 @@ defineExpose({
ref="treeRef"
:props="defaultProps"
:data="treeData"
v-model="treeCheck"
node-key="id"
default-expand-all
highlight-current
:show-checkbox="showCheckbox"
:default-checked-keys="treeCheck"
check-strictly
@check="checkHandle"
@node-click="clickHandle"
/>

View File

@@ -1,5 +1,6 @@
<script lang="ts" setup>
import { $t } from "@/common/languages";
import { computed } from "vue";
const props = defineProps({
authShowFunc: Function,
@@ -9,6 +10,16 @@ const emit = defineEmits<{ "operate-button-click": [eventName: string, row: any]
const buttonList = useRoute().meta.toolButtonAuth ?? [];
// 根据按钮数量计算列宽
// 中文2字按钮约40px4字按钮约60px
// 英文1词按钮约40px3词按钮约80px
// 间隙每个按钮间8px
const columnWidth = computed(() => {
const count = buttonList.length;
// 基础边距20px + 每个按钮平均45px + 间隙
return 20 + count * 45 + (count - 1) * 8;
});
const authShowFunc = (row: any, button: globalThis.ButtonProp) => {
if (props.authShowFunc !== undefined) {
return props.authShowFunc(row, button);
@@ -17,18 +28,49 @@ const authShowFunc = (row: any, button: globalThis.ButtonProp) => {
};
</script>
<template>
<el-table-column :label="$t('_prop.common.operate')" v-if="buttonList?.length !== 0">
<el-table-column
:label="$t('_prop.common.operate')"
v-if="buttonList?.length !== 0"
:min-width="columnWidth"
align="center"
fixed="right"
class-name="operate-column"
>
<template #default="{ row }">
<template v-for="button of buttonList" :key="button.buttonName">
<el-button
v-if="authShowFunc(row, button)"
:type="button.colorType || 'primary'"
@click="emit('operate-button-click', button.eventName, row)"
link
>
{{ $t("_button." + button.eventName) }}
</el-button>
</template>
<div class="operate-buttons">
<template v-for="button of buttonList" :key="button.buttonName">
<el-button
v-if="authShowFunc(row, button)"
:type="button.colorType || 'primary'"
@click="emit('operate-button-click', button.eventName, row)"
link
size="small"
class="operate-btn"
>
{{ $t("_button." + button.eventName) }}
</el-button>
</template>
</div>
</template>
</el-table-column>
</template>
<style scoped>
.operate-buttons {
display: flex;
flex-wrap: nowrap;
gap: 4px;
align-items: center;
justify-content: center;
}
.operate-btn {
padding: 2px 4px;
font-size: 13px;
white-space: nowrap;
}
.operate-btn:hover {
transform: translateY(-1px);
transition: transform 0.2s ease;
}
</style>

View File

@@ -48,7 +48,7 @@ const getTagType = (code: number | null): string => {
};
</script>
<template>
<el-table-column :label="$t('_prop.common.status')" :prop="statusParamName">
<el-table-column :label="$t('_prop.common.status')" :prop="statusParamName" min-width="100" align="center">
<template #default="scope">
<!-- 没有权限按钮时显示带框标签 -->
<span v-if="buttonList.length === 0">

View File

@@ -1,5 +1,5 @@
<script lang="ts" setup>
import { ref, reactive, nextTick } from "vue";
import { ref, reactive, nextTick, computed } from "vue";
import type { PropType } from "vue";
import BasePageableTable from "../base-pageable-table/BasePageableTable.vue";
import type { SearcherProp } from "../search-bar/SearchBarType";
@@ -19,6 +19,14 @@ const props = defineProps({
itemIdName: String,
itemFieldName: String,
rowKey: { type: String, default: "id" },
useAuth: { type: Boolean, default: true },
});
const buttonList = useRoute().meta.toolButtonAuth ?? [];
const hasShowItemAuth = computed(() => {
if (!props.useAuth) return true;
return buttonList.some(item => item.eventName === "showItem");
});
interface Emits {
@@ -209,7 +217,7 @@ defineExpose({
</template>
<template #columns>
<el-table-column v-if="itemUrl || itemFieldName" type="expand" fixed="left" width="50">
<el-table-column v-if="(itemUrl || itemFieldName) && hasShowItemAuth" type="expand" fixed="left" width="50">
<template #default="{ row, expanded }">
<div :class="['item-container', { 'is-expanded': expanded }]">
<div v-if="loadingItems[getRowKey(row)]" class="loading-state">

View File

@@ -19,6 +19,7 @@ const props = defineProps({
treeSideNodeName: String,
treeSideTitle: String,
});
const emit = defineEmits(["update:current-node"]);
const page = ref(1);
const pageSize = ref(20);
const total = ref(0);
@@ -32,6 +33,10 @@ const resetClick = () => {
loadData();
};
watch(treeSelect, newVal => {
emit("update:current-node", newVal);
});
const loadData = async () => {
if (props.data !== undefined) {
total.value = props.data.length;

View File

@@ -181,6 +181,8 @@ export const useRouteFn = () => {
// 这两个顺序不能互换,因为 translateTitle 函数需要 route.meta.useI18n
if (useI18n === undefined && routeUseI18n !== undefined) route.meta.useI18n = routeUseI18n;
if (!isFunction(title)) {
// 保留原始 title用于语言切换时重新翻译
route.meta._rawTitle = String(title);
route.meta.title = translateTitle(String(title), route.name as string, route.meta.useI18n);
}

View File

@@ -14,11 +14,15 @@ type Route = RouteLocationNormalizedLoaded | RouterConfigRaw;
export const formatTitle = (route: Route, reTranslate = false) => {
// 取消 meta 响应式
const meta: MetaProps = { ...route.meta };
const { title: routeTitle, useI18n } = meta;
const { title: routeTitle, useI18n, _rawTitle } = meta;
const name = route.name as string;
let title = routeTitle ?? "";
if (reTranslate && !isFunction(title)) title = translateTitle(String(title), name, useI18n);
// 重新翻译时,优先使用原始的 titlei18n key
if (reTranslate && !isFunction(title)) {
const rawTitle = _rawTitle || String(title);
title = translateTitle(rawTitle, name, useI18n);
}
if (title && !isFunction(title)) return String(title);
if (isFunction(title)) title = title(route as RouteLocationNormalizedLoaded);

View File

@@ -40,6 +40,10 @@ declare global {
* 是否是动态路由,在编译阶段自动生成
*/
_dynamic?: boolean;
/**
* 原始的 titlei18n key用于语言切换时重新翻译
*/
_rawTitle?: string;
/**
* 可访问该页面的权限数组,当前路由设置的权限会影响子路由
*/

View File

@@ -175,6 +175,16 @@ const remove = (row: any) => {
useRemove(removeUrl, row.id, "_message.production.finishedproductreceipt.delete_message");
};
// 成品入库状态标签类型映射
const getFinishedProductReceiptStatusTagType = (code: number | null): string => {
const tagTypeMap: Record<number, string> = {
1: "warning", // 待出货
3: "primary", // 出货中
4: "success", // 已完成
};
return tagTypeMap[code ?? -1] || "info";
};
const outstock = async (row: any) => {
// 检查单据状态,只有待出货和出货中状态才能出货
if (row.formStatus !== 1 && row.formStatus !== 3) {
@@ -320,6 +330,7 @@ const handleReset = () => {
<DefaultStatusSwitchColumn
status-param-name="formStatus"
:status-label-mapping="getFinishedProductReceiptStatusLabel"
:tag-type-mapping="getFinishedProductReceiptStatusTagType"
/>
<DefaultOperateButtonColumn @operate-button-click="operateButtonClick" :auth-show-func="authShowFunc" />
</template>

View File

@@ -146,6 +146,22 @@ const remove = (row: any) => {
useRemove(removeUrl, row.id, "_message.production.finishedproductshipment.delete_message");
};
// 表单状态标签类型映射
const getFormStatusTagType = (code: number | null): string => {
const tagTypeMap: Record<number, string> = {
0: "info", // 未审核
1: "success", // 已审核
2: "danger", // 已驳回
3: "warning", // 未完成
4: "success", // 已完成
5: "primary", // 入库中
6: "info", // 已导入
7: "warning", // 审核中
8: "danger", // 已退回
};
return tagTypeMap[code ?? -1] || "info";
};
const approve = (row: any) => {
ElMessageBox.confirm($t("_message.production.finishedproductshipment.approve_confirm"), $t("_level.warning"), {
confirmButtonText: $t("_button.confirm"),
@@ -265,7 +281,11 @@ const handleReset = () => {
</el-table-column>
<el-table-column :label="$t('_prop.production.finishedproductshipment.formMark')" prop="formMark" />
<el-table-column :label="$t('_prop.common.createDate')" prop="createDate" :formatter="formatDate" />
<DefaultStatusSwitchColumn status-param-name="formStatus" :status-label-mapping="getFormStatusLabel" />
<DefaultStatusSwitchColumn
status-param-name="formStatus"
:status-label-mapping="getFormStatusLabel"
:tag-type-mapping="getFormStatusTagType"
/>
<DefaultOperateButtonColumn @operate-button-click="operateButtonClick" :auth-show-func="authShowFunc" />
</template>
<template #item-content="{ itemData }">

View File

@@ -13,6 +13,7 @@ import { ElMessage, ElMessageBox } from "element-plus";
import { ref } from "vue";
import { generateDucumentNo } from "@/common/utils/document-no-generator/document-no-generator";
import { DocumentType } from "@/common/enums/DocumentType";
import BaseItemDialog from "@/components/base/base-item-dialog/BaseItemDialog.vue";
/**
* 必须要的变量
@@ -28,7 +29,8 @@ const generateProductionReturnUrl = "/production/productionissue/productionRetur
*/
const tableRef = ref<InstanceType<typeof ExpandablePageableTable> | null>(null);
const { getFormStatusLabel } = useStatus();
const { useApprove, useReject } = usePage(tableRef);
const { useApprove, useReject, useGeneralPageRef } = usePage(tableRef);
const { itemVisible, itemParentId } = useGeneralPageRef();
const productionReturnRef = ref<InstanceType<typeof BaseTableForm>>();
const productionReturnVisible = ref(false);
const productionReturnTableData = ref([]);
@@ -68,6 +70,11 @@ const productionReturn = async (row: any) => {
}
};
const showItem = (row: any) => {
itemParentId.value = row.id;
itemVisible.value = true;
};
const operateButtonClick = (eventName: string, row: any) => {
switch (eventName) {
case "approve":
@@ -79,6 +86,9 @@ const operateButtonClick = (eventName: string, row: any) => {
case "productionReturn":
productionReturn(row);
break;
case "showItem":
showItem(row);
break;
}
};
const authShowFunc = (row: any, button: globalThis.ButtonProp) => {
@@ -92,6 +102,22 @@ const authShowFunc = (row: any, button: globalThis.ButtonProp) => {
return true;
};
// 表单状态标签类型映射
const getFormStatusTagType = (code: number | null): string => {
const tagTypeMap: Record<number, string> = {
0: "info", // 未审核
1: "success", // 已审核
2: "danger", // 已驳回
3: "warning", // 未完成
4: "success", // 已完成
5: "primary", // 入库中
6: "info", // 已导入
7: "warning", // 审核中
8: "danger", // 已退回
};
return tagTypeMap[code ?? -1] || "info";
};
const generateProductionReturn = async () => {
if (!productionReturnTableData.value || productionReturnTableData.value.length === 0) {
ElMessage.warning($t("_message.production.production_issue.no_return_data"));
@@ -165,7 +191,11 @@ const generateProductionReturn = async () => {
<el-table-column :label="$t('_prop.production.production_issue.storeName')" prop="storeName" />
<el-table-column :label="$t('_prop.production.production_issue.formMark')" prop="formMark" />
<el-table-column :label="$t('_prop.common.createDate')" prop="createDate" :formatter="formatDate" />
<DefaultStatusSwitchColumn status-param-name="formStatus" :status-label-mapping="getFormStatusLabel" />
<DefaultStatusSwitchColumn
status-param-name="formStatus"
:status-label-mapping="getFormStatusLabel"
:tag-type-mapping="getFormStatusTagType"
/>
<DefaultOperateButtonColumn @operate-button-click="operateButtonClick" :auth-show-func="authShowFunc" />
</template>
<template #item-content="{ itemData }">
@@ -201,4 +231,18 @@ const generateProductionReturn = async () => {
</el-button>
</template>
</BaseTableForm>
<BaseItemDialog
:title="$t('_title.production.production_issue.showItem')"
v-model:visible="itemVisible"
:url="getItemUrl"
parent-param-name="issueId"
v-model:parent-param-value="itemParentId"
>
<template #columns>
<el-table-column :label="$t('_prop.warehouse.warehouse_item.partNumber')" prop="partNumber" />
<el-table-column :label="$t('_prop.warehouse.warehouse_item.productSpecs')" prop="productSpecs" />
<el-table-column :label="$t('_prop.production.production_issue.requiredQty')" prop="requiredQty" />
<el-table-column :label="$t('_prop.production.production_issue.actualQty')" prop="actualQty" />
</template>
</BaseItemDialog>
</template>

View File

@@ -152,6 +152,17 @@ const viewMaterialShortage = async () => {
dialogVisible.value = true;
};
// 生产计划状态标签类型映射
const getProductionPlanStatusTagType = (code: number | null): string => {
const tagTypeMap: Record<number, string> = {
0: "info", // 未完成
1: "success", // 已完成
2: "warning", // 审核中
3: "primary", // 已审核
};
return tagTypeMap[code ?? -1] || "info";
};
const generatePurchasePlan = (selectedShortageRows: any[]) => {
ElMessageBox.confirm($t("_message.production.production_plan.generate_purchase_plan_confirm"), $t("_level.warning"), {
confirmButtonText: $t("_button.confirm"),
@@ -408,6 +419,7 @@ const handleReset = () => {
<DefaultStatusSwitchColumn
status-param-name="productionStatus"
:status-label-mapping="getProductionPlanStatusLabel"
:tag-type-mapping="getProductionPlanStatusTagType"
/>
<DefaultOperateButtonColumn @operate-button-click="operateButtonClick" />
</template>

View File

@@ -8,6 +8,7 @@ import DefaultStatusSwitchColumn from "@/components/base/default-column/DefaultS
import { useStatus } from "@/common/languages/mapping/base-info-mapping";
import { usePage } from "@/composables/use-page";
import { ref } from "vue";
import BaseItemDialog from "@/components/base/base-item-dialog/BaseItemDialog.vue";
/**
* 必须要的变量
@@ -22,7 +23,8 @@ const rejectUrl = "/production/productionreturn/rejectProductionReturn";
*/
const tableRef = ref<InstanceType<typeof ExpandablePageableTable> | null>(null);
const { getFormStatusLabel } = useStatus();
const { useApprove, useReject } = usePage(tableRef);
const { useApprove, useReject, useGeneralPageRef } = usePage(tableRef);
const { itemVisible, itemParentId } = useGeneralPageRef();
/**
* 可以自定义的变量
*/
@@ -33,6 +35,11 @@ const approve = (row: any) => {
const reject = (row: any) => {
useReject(rejectUrl, row.id);
};
const showItem = (row: any) => {
itemParentId.value = row.id;
itemVisible.value = true;
};
const operateButtonClick = (eventName: string, row: any) => {
switch (eventName) {
case "approve":
@@ -41,6 +48,9 @@ const operateButtonClick = (eventName: string, row: any) => {
case "reject":
reject(row);
break;
case "showItem":
showItem(row);
break;
}
};
const authShowFunc = (row: any, button: globalThis.ButtonProp) => {
@@ -48,6 +58,22 @@ const authShowFunc = (row: any, button: globalThis.ButtonProp) => {
if (row.formStatus === 1 && button.eventName === "approve") return false;
return true;
};
// 表单状态标签类型映射
const getFormStatusTagType = (code: number | null): string => {
const tagTypeMap: Record<number, string> = {
0: "info", // 未审核
1: "success", // 已审核
2: "danger", // 已驳回
3: "warning", // 未完成
4: "success", // 已完成
5: "primary", // 入库中
6: "info", // 已导入
7: "warning", // 审核中
8: "danger", // 已退回
};
return tagTypeMap[code ?? -1] || "info";
};
</script>
<template>
<ExpandablePageableTable
@@ -66,7 +92,11 @@ const authShowFunc = (row: any, button: globalThis.ButtonProp) => {
<el-table-column :label="$t('_prop.production.production_return.storeName')" prop="storeName" />
<el-table-column :label="$t('_prop.production.production_return.formMark')" prop="formMark" />
<el-table-column :label="$t('_prop.common.createDate')" prop="createDate" :formatter="formatDate" />
<DefaultStatusSwitchColumn status-param-name="formStatus" :status-label-mapping="getFormStatusLabel" />
<DefaultStatusSwitchColumn
status-param-name="formStatus"
:status-label-mapping="getFormStatusLabel"
:tag-type-mapping="getFormStatusTagType"
/>
<DefaultOperateButtonColumn @operate-button-click="operateButtonClick" :auth-show-func="authShowFunc" />
</template>
<template #item-content="{ itemData }">
@@ -77,4 +107,17 @@ const authShowFunc = (row: any, button: globalThis.ButtonProp) => {
</el-table>
</template>
</ExpandablePageableTable>
<BaseItemDialog
:title="$t('_title.production.production_return.showItem')"
v-model:visible="itemVisible"
:url="getItemUrl"
parent-param-name="returnId"
v-model:parent-param-value="itemParentId"
>
<template #columns>
<el-table-column :label="$t('_prop.warehouse.warehouse_item.partNumber')" prop="partNumber" />
<el-table-column :label="$t('_prop.warehouse.warehouse_item.productSpecs')" prop="productSpecs" />
<el-table-column :label="$t('_prop.production.production_return.returnQty')" prop="returnQty" />
</template>
</BaseItemDialog>
</template>

View File

@@ -343,6 +343,22 @@ const authShowFunc = (row: any, button: globalThis.ButtonProp) => {
}
return true;
};
// 表单状态标签类型映射
const getFormStatusTagType = (code: number | null): string => {
const tagTypeMap: Record<number, string> = {
0: "info", // 未审核
1: "success", // 已审核
2: "danger", // 已驳回
3: "warning", // 未完成
4: "success", // 已完成
5: "primary", // 入库中
6: "info", // 已导入
7: "warning", // 审核中
8: "danger", // 已退回
};
return tagTypeMap[code ?? -1] || "info";
};
</script>
<template>
<ExpandablePageableTable
@@ -366,7 +382,11 @@ const authShowFunc = (row: any, button: globalThis.ButtonProp) => {
</el-table-column>
<el-table-column :label="$t('_prop.purchase.purchase_order.formMark')" prop="formMark" />
<el-table-column :label="$t('_prop.common.createDate')" prop="createDate" width="180" :formatter="formatDate" />
<DefaultStatusSwitchColumn status-param-name="formStatus" :status-label-mapping="getFormStatusLabel" />
<DefaultStatusSwitchColumn
status-param-name="formStatus"
:status-label-mapping="getFormStatusLabel"
:tag-type-mapping="getFormStatusTagType"
/>
<DefaultOperateButtonColumn @operate-button-click="operateButtonClick" :auth-show-func="authShowFunc" />
</template>
<template #item-content="{ itemData }">

View File

@@ -111,6 +111,16 @@ const authShowFunc = (row: any, button: globalThis.ButtonProp) => {
return true;
};
// 采购计划状态标签类型映射
const getPurchasePlanStatusTagType = (code: number | null): string => {
const tagTypeMap: Record<number, string> = {
0: "warning", // 待处理
1: "primary", // 进行中
2: "success", // 已完成
};
return tagTypeMap[code ?? -1] || "info";
};
const operateButtonClick = (eventName: string, row: any) => {
switch (eventName) {
case "edit":
@@ -364,7 +374,11 @@ const mappingConfig: FieldMappingConfig = {
<el-table-column :label="$t('_prop.purchase.purchase_plan.planNo')" prop="planNo" />
<el-table-column :label="$t('_prop.purchase.purchase_plan.planName')" prop="planName" />
<el-table-column :label="$t('_prop.purchase.purchase_plan.storeName')" prop="storeName" />
<DefaultStatusSwitchColumn status-param-name="planStatus" :status-label-mapping="getPurchasePlanStatusLabel" />
<DefaultStatusSwitchColumn
status-param-name="planStatus"
:status-label-mapping="getPurchasePlanStatusLabel"
:tag-type-mapping="getPurchasePlanStatusTagType"
/>
<el-table-column :label="$t('_prop.purchase.purchase_plan.remask')" prop="remask" />
<el-table-column :label="$t('_prop.common.createDate')" prop="createDate" :formatter="formatDate" />
<DefaultOperateButtonColumn @operate-button-click="operateButtonClick" :auth-show-func="authShowFunc" />

View File

@@ -240,6 +240,22 @@ const calculateTotalPrice = (row: any) => {
row.totalPrice = Number((row.saleCount * row.price).toFixed(2));
}
};
// 表单状态标签类型映射
const getFormStatusTagType = (code: number | null): string => {
const tagTypeMap: Record<number, string> = {
0: "info", // 未审核
1: "success", // 已审核
2: "danger", // 已驳回
3: "warning", // 未完成
4: "success", // 已完成
5: "primary", // 入库中
6: "info", // 已导入
7: "warning", // 审核中
8: "danger", // 已退回
};
return tagTypeMap[code ?? -1] || "info";
};
</script>
<template>
<ExpandablePageableTable
@@ -264,6 +280,7 @@ const calculateTotalPrice = (row: any) => {
<DefaultStatusSwitchColumn
status-param-name="formStatus"
:status-label-mapping="getFormStatusLabel"
:tag-type-mapping="getFormStatusTagType"
:switch-on-value="1"
:switch-off-value="0"
/>

View File

@@ -85,6 +85,15 @@ const handleSuccess = () => {
};
const getDialogTitle = computed(() => (isEdit.value ? "编辑用户" : "新建用户"));
// 通用状态标签类型映射(启用/禁用)
const getCommonStatusTagType = (code: number | null): string => {
const tagTypeMap: Record<number, string> = {
0: "success", // 启用
1: "info", // 禁用
};
return tagTypeMap[code ?? -1] || "info";
};
</script>
<template>
<BasePageableTable ref="tableRef" :url="url" :searchers="searchers">
@@ -106,7 +115,7 @@ const getDialogTitle = computed(() => (isEdit.value ? "编辑用户" : "新建
</template>
</el-table-column>
<el-table-column label="创建时间" prop="createDate" min-width="160" />
<DefaultStatusSwitchColumn />
<DefaultStatusSwitchColumn :tag-type-mapping="getCommonStatusTagType" />
<DefaultOperateButtonColumn />
</template>
</BasePageableTable>

View File

@@ -9,6 +9,7 @@ import { formatDate } from "@/common/utils/format-utils";
import TreeSidePageableTable from "@/components/base/treeside-pageable-table/TreeSidePageableTable.vue";
import DefaultStatusSwitchColumn from "@/components/base/default-column/DefaultStatusSwitchColumn.vue";
import StatusSwitch from "@/components/base/base-switch/StatusSwitch.vue";
import BaseTreeSelect from "@/components/base/base-tree-select/BaseTreeSelect.vue";
/**
* 必须要的变量
@@ -68,12 +69,21 @@ const tableRef = ref<InstanceType<typeof TreeSidePageableTable> | null>(null);
const { useAdd, useEdit, useRemove, useGeneralPageRef } = usePage(tableRef);
const { title, visible, formType, form } = useGeneralPageRef();
/**
* 左侧树当前选中的节点
*/
const currentTreeNode = ref<any>(null);
/**
* 可以自定义的变量
*/
const add = () => {
form.value = {};
// 如果左侧树有选中节点,自动设置父级权限
if (currentTreeNode.value && currentTreeNode.value.id !== undefined) {
form.value.parentId = currentTreeNode.value.id;
}
title.value = "_title.systemset.permission.add";
visible.value = true;
formType.value = false;
@@ -122,6 +132,15 @@ const formatPermissionType = (row: any) => {
const type = permissionTypeOptions.find(item => item.code === row.permissionType);
return type?.description || "";
};
// 通用状态标签类型映射(启用/禁用)
const getCommonStatusTagType = (code: number | null): string => {
const tagTypeMap: Record<number, string> = {
0: "success", // 启用
1: "info", // 禁用
};
return tagTypeMap[code ?? -1] || "info";
};
</script>
<template>
@@ -132,6 +151,7 @@ const formatPermissionType = (row: any) => {
:tree-side-url="treeSideUrl"
tree-side-param-name="parentId"
tree-side-node-name="id"
@update:current-node="currentTreeNode = $event"
>
<template #tool-button>
<DefaultToolButton @top-button-click="topButtonClick" />
@@ -158,7 +178,7 @@ const formatPermissionType = (row: any) => {
</el-tag>
</template>
</el-table-column>
<DefaultStatusSwitchColumn :url="statusUrl" />
<DefaultStatusSwitchColumn :url="statusUrl" :tag-type-mapping="getCommonStatusTagType" />
<el-table-column :label="$t('_prop.common.createDate')" prop="createDate" :formatter="formatDate" />
<DefaultOperateButtonColumn @operate-button-click="operateButtonClick" />
</template>
@@ -274,11 +294,10 @@ const formatPermissionType = (row: any) => {
</el-row>
<el-form-item :label="$t('_prop.systemset.permission.parentId')" prop="parentId">
<el-input-number
<BaseTreeSelect
v-model="form.parentId"
:min="0"
:placeholder="$t('_message.systemset.permission.input_parentId')"
style="width: 100%"
:url="treeSideUrl"
:placeholder="$t('_message.systemset.permission.select_parentId')"
/>
</el-form-item>
</template>

View File

@@ -87,6 +87,15 @@ const operateButtonClick = (eventName: string, row: any) => {
break;
}
};
// 通用状态标签类型映射(启用/禁用)
const getCommonStatusTagType = (code: number | null): string => {
const tagTypeMap: Record<number, string> = {
0: "success", // 启用
1: "info", // 禁用
};
return tagTypeMap[code ?? -1] || "info";
};
</script>
<template>
<TreeSidePageableTable
@@ -109,7 +118,7 @@ const operateButtonClick = (eventName: string, row: any) => {
<el-table-column :label="$t('_prop.systemset.syschannel.channelType')" prop="channelType" />
<el-table-column :label="$t('_prop.systemset.syschannel.sort')" prop="sort" />
<el-table-column :label="$t('_prop.systemset.syschannel.show')" prop="show" />
<DefaultStatusSwitchColumn :url="statusUrl" />
<DefaultStatusSwitchColumn :url="statusUrl" :tag-type-mapping="getCommonStatusTagType" />
<el-table-column :label="$t('_prop.common.createDate')" prop="createDate" :formatter="formatDate" />
<DefaultOperateButtonColumn @operate-button-click="operateButtonClick" />
</template>

View File

@@ -82,6 +82,15 @@ const operateButtonClick = (eventName: string, row: any) => {
break;
}
};
// 通用状态标签类型映射(启用/禁用)
const getCommonStatusTagType = (code: number | null): string => {
const tagTypeMap: Record<number, string> = {
0: "success", // 启用
1: "info", // 禁用
};
return tagTypeMap[code ?? -1] || "info";
};
</script>
<template>
<BasePageableTable :url="getPageUrl" :searchers="searchers" ref="tableRef">
@@ -93,7 +102,7 @@ const operateButtonClick = (eventName: string, row: any) => {
<el-table-column :label="$t('_prop.systemset.sysrole.roleType')" prop="roleType" :formatter="formatRoleType" />
<el-table-column :label="$t('_prop.common.remark')" prop="remark" />
<el-table-column :label="$t('_prop.common.createDate')" prop="createDate" :formatter="formatDate" />
<DefaultStatusSwitchColumn :url="statusUrl" />
<DefaultStatusSwitchColumn :url="statusUrl" :tag-type-mapping="getCommonStatusTagType" />
<DefaultOperateButtonColumn @operate-button-click="operateButtonClick" />
</template>
</BasePageableTable>

View File

@@ -175,6 +175,15 @@ const getUserTypeLabel = (type: number) => {
const option = userTypeOptions.find(item => item.code === type);
return option?.description || "-";
};
// 通用状态标签类型映射(启用/禁用)
const getCommonStatusTagType = (code: number | null): string => {
const tagTypeMap: Record<number, string> = {
0: "success", // 启用
1: "info", // 禁用
};
return tagTypeMap[code ?? -1] || "info";
};
</script>
<template>
<BasePageableTable :url="getPageUrl" :searchers="searchers" ref="tableRef">
@@ -191,7 +200,7 @@ const getUserTypeLabel = (type: number) => {
:formatter="(row: any) => getUserTypeLabel(row.userType)"
/>
<el-table-column :label="$t('_prop.common.createDate')" prop="createDate" :formatter="formatDate" />
<DefaultStatusSwitchColumn :url="statusUrl" />
<DefaultStatusSwitchColumn :url="statusUrl" :tag-type-mapping="getCommonStatusTagType" />
<DefaultOperateButtonColumn @operate-button-click="operateButtonClick" />
</template>
</BasePageableTable>

View File

@@ -13,6 +13,7 @@ import DefaultStatusSwitchColumn from "@/components/base/default-column/DefaultS
import { useStatus } from "@/common/languages/mapping/base-info-mapping";
import { generateDucumentNo } from "@/common/utils/document-no-generator/document-no-generator";
import { DocumentType } from "@/common/enums/DocumentType";
import BaseItemDialog from "@/components/base/base-item-dialog/BaseItemDialog.vue";
/**
* 必须要的变量
@@ -137,8 +138,25 @@ const validateProductCount = (index: number) => {
*/
const tableRef = ref<InstanceType<typeof ExpandablePageableTable> | null>(null);
const { useAdd, useEdit, useRemove, useGeneralPageRef, useApprove, useReject } = usePage(tableRef);
const { title, visible, formType, form } = useGeneralPageRef();
const { title, visible, formType, form, itemVisible, itemParentId } = useGeneralPageRef();
const { getFormStatusLabel } = useStatus();
// 表单状态标签类型映射
const getFormStatusTagType = (code: number | null): string => {
const tagTypeMap: Record<number, string> = {
0: "info", // 未审核
1: "success", // 已审核
2: "danger", // 已驳回
3: "warning", // 未完成
4: "success", // 已完成
5: "primary", // 入库中
6: "info", // 已导入
7: "warning", // 审核中
8: "danger", // 已退回
};
return tagTypeMap[code ?? -1] || "info";
};
const baseFormWithTableRef = ref<InstanceType<typeof BaseFormWithTable>>();
const warehouseOptions = ref<Array<{ label: string; value: number }>>([]);
@@ -189,6 +207,11 @@ const remove = (row: any) => {
useRemove(removeUrl, row.id, "_message.warehouse.stocktransferorder.delete_message");
};
const showItem = (row: any) => {
itemParentId.value = row.id;
itemVisible.value = true;
};
const approve = (row: any) => {
useApprove(approveUrl, row.id, "_message.warehouse.stocktransferorder.approve_confirm");
};
@@ -226,6 +249,9 @@ const operateButtonClick = (eventName: string, row: any) => {
case "remove":
remove(row);
break;
case "showItem":
showItem(row);
break;
case "approve":
approve(row);
break;
@@ -261,7 +287,11 @@ const authShowFunc = (row: any, button: globalThis.ButtonProp) => {
<el-table-column :label="$t('_prop.warehouse.stocktransferorder.outStoreName')" prop="outStoreName" />
<el-table-column :label="$t('_prop.warehouse.stocktransferorder.formMark')" prop="formMark" />
<el-table-column :label="$t('_prop.common.createDate')" prop="createDate" :formatter="formatDate" />
<DefaultStatusSwitchColumn status-param-name="formStatus" :status-label-mapping="getFormStatusLabel" />
<DefaultStatusSwitchColumn
status-param-name="formStatus"
:status-label-mapping="getFormStatusLabel"
:tag-type-mapping="getFormStatusTagType"
/>
<DefaultOperateButtonColumn @operate-button-click="operateButtonClick" :auth-show-func="authShowFunc" />
</template>
<template #item-content="{ itemData }">
@@ -368,4 +398,18 @@ const authShowFunc = (row: any, button: globalThis.ButtonProp) => {
</el-table-column>
</template>
</BaseFormWithTable>
<BaseItemDialog
:title="$t('_title.warehouse.stocktransferorder.showItem')"
v-model:visible="itemVisible"
:url="getItemUrl"
parent-param-name="orderId"
v-model:parent-param-value="itemParentId"
>
<template #columns>
<el-table-column :label="$t('_prop.warehouse.stocktransferorder.partNumber')" prop="partNumber" width="150" />
<el-table-column :label="$t('_prop.warehouse.stocktransferorder.productSpecs')" prop="productSpecs" width="200" />
<el-table-column :label="$t('_prop.warehouse.stocktransferorder.productCount')" prop="productCount" width="200" />
<el-table-column :label="$t('_prop.warehouse.stocktransferorder.demandCount')" prop="demandCount" width="200" />
</template>
</BaseItemDialog>
</template>

View File

@@ -162,6 +162,22 @@ const authShowFunc = (row: any, button: globalThis.ButtonProp) => {
return true;
};
// 表单状态标签类型映射
const getFormStatusTagType = (code: number | null): string => {
const tagTypeMap: Record<number, string> = {
0: "info", // 未审核
1: "success", // 已审核
2: "danger", // 已驳回
3: "warning", // 未完成
4: "success", // 已完成
5: "primary", // 入库中
6: "info", // 已导入
7: "warning", // 审核中
8: "danger", // 已退回
};
return tagTypeMap[code ?? -1] || "info";
};
const submit = (form: any, formRef: FormInstance | undefined) => {
const targetRef = formRef || (baseFormWithTableRef.value as any)?.baseFormRef;
if (!targetRef) return;
@@ -199,7 +215,11 @@ const submit = (form: any, formRef: FormInstance | undefined) => {
<el-table-column :label="$t('_prop.warehouse.warehousereceipt.formName')" prop="formName" />
<el-table-column :label="$t('_prop.warehouse.warehousereceipt.formMark')" prop="formMark" />
<el-table-column :label="$t('_prop.common.createDate')" prop="createDate" :formatter="formatDate" />
<DefaultStatusSwitchColumn status-param-name="formStatus" :status-label-mapping="getFormStatusLabel" />
<DefaultStatusSwitchColumn
status-param-name="formStatus"
:status-label-mapping="getFormStatusLabel"
:tag-type-mapping="getFormStatusTagType"
/>
<DefaultOperateButtonColumn @operate-button-click="operateButtonClick" :auth-show-func="authShowFunc" />
</template>
<template #item-content="{ itemData }">
@@ -331,9 +351,24 @@ const submit = (form: any, formRef: FormInstance | undefined) => {
v-model:parent-param-value="itemParentId"
>
<template #columns>
<el-table-column :label="$t('_prop.warehouse.warehousereceipt.partNumber')" prop="partNumber" />
<el-table-column :label="$t('_prop.warehouse.warehousereceipt.productSpec')" prop="productSpec" />
<el-table-column :label="$t('_prop.warehouse.warehousereceipt.productCount')" prop="productCount" />
<el-table-column
:label="$t('_prop.warehouse.warehousereceipt.partNumber')"
prop="partNumber"
min-width="180"
show-overflow-tooltip
/>
<el-table-column
:label="$t('_prop.warehouse.warehousereceipt.productSpec')"
prop="productSpec"
min-width="200"
show-overflow-tooltip
/>
<el-table-column
:label="$t('_prop.warehouse.warehousereceipt.productCount')"
prop="productCount"
min-width="120"
align="right"
/>
</template>
</BaseItemDialog>
</template>