完善基础数据
This commit is contained in:
@@ -52,6 +52,12 @@ export const componentMap = {
|
||||
'base/guige/index': () => import('../views/base/guige/index.vue'),
|
||||
'base/dingjizhibiao/index': () => import('../views/base/dingjizhibiao/index.vue'),
|
||||
'base/jizuchetai/index': () => import('../views/base/jizuchetai/index.vue'),
|
||||
'base/worktype/index': () => import('../views/base/worktype/index.vue'),
|
||||
'base/zhangqi/index': () => import('../views/base/zhangqi/index.vue'),
|
||||
'base/registry/index': () => import('../views/base/registry/index.vue'),
|
||||
'base/yuanliaozhuangkou/index': () => import('../views/base/yuanliaozhuangkou/index.vue'),
|
||||
'base/wuzimulu/index': () => import('../views/base/wuzimulu/index.vue'),
|
||||
'base/gongyingshang/index': () => import('../views/base/gongyingshang/index.vue'),
|
||||
'prod/lsjl/index': () => import('../views/prod/lsjl/index.vue'),
|
||||
'prod/xdjy/index': () => import('../views/prod/xdjy/index.vue'),
|
||||
'prod/hbjy/index': () => import('../views/prod/hbjy/index.vue'),
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import request from '../../../utils/request';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
|
||||
// 业务类型 / 分组(与后端 Gys_Type_Enum / Gys_Group_Enum 一致)
|
||||
const typeOptions = [
|
||||
{ label: '干茧', value: 1 },
|
||||
{ label: '配件', value: 2 },
|
||||
{ label: '副产品', value: 3 },
|
||||
{ label: '生丝', value: 4 }
|
||||
];
|
||||
const groupOptions = [
|
||||
{ label: '供应商', value: 1 },
|
||||
{ label: '客户', value: 2 }
|
||||
];
|
||||
// 销售类型(Gys_SaleType_Enum:内销=1、外销=2;历史数据中的 0 表示未设置)
|
||||
const saleTypeOptions = [
|
||||
{ label: '未设置', value: 0 },
|
||||
{ label: '内销', value: 1 },
|
||||
{ label: '外销', value: 2 }
|
||||
];
|
||||
|
||||
const list = ref([]);
|
||||
const total = ref(0);
|
||||
const loading = ref(false);
|
||||
const query = reactive({ type: 1, saleType: null, like: '', pageIndex: 1, pageSize: 20 });
|
||||
|
||||
const dialogVisible = ref(false);
|
||||
const dialogTitle = ref('新增往来单位');
|
||||
const isEdit = ref(false);
|
||||
const editId = ref(null);
|
||||
const form = reactive({
|
||||
name: '',
|
||||
type: 1,
|
||||
group: 1,
|
||||
saleType: 0,
|
||||
lianxiren: '',
|
||||
phone: '',
|
||||
address: '',
|
||||
pinyin: ''
|
||||
});
|
||||
|
||||
const vendorGroupTypes = [3, 4]; // 副产品 / 生丝 → 默认分组为"客户"
|
||||
|
||||
async function loadList() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await request.get('/sys/gongyingshang', {
|
||||
params: {
|
||||
type: query.type,
|
||||
saleType: query.saleType === null ? undefined : query.saleType,
|
||||
like: query.like || undefined,
|
||||
pageIndex: query.pageIndex,
|
||||
pageSize: query.pageSize
|
||||
}
|
||||
});
|
||||
list.value = res.data.items || [];
|
||||
total.value = res.data.total || 0;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
query.pageIndex = 1;
|
||||
loadList();
|
||||
}
|
||||
|
||||
function handleTypeChange() {
|
||||
if (vendorGroupTypes.includes(form.type)) form.group = 2;
|
||||
else form.group = 1;
|
||||
}
|
||||
|
||||
function openCreate() {
|
||||
isEdit.value = false;
|
||||
dialogTitle.value = '新增往来单位';
|
||||
Object.assign(form, {
|
||||
name: '',
|
||||
type: 1,
|
||||
group: 1,
|
||||
saleType: 0,
|
||||
lianxiren: '',
|
||||
phone: '',
|
||||
address: '',
|
||||
pinyin: ''
|
||||
});
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
|
||||
function openEdit(row) {
|
||||
isEdit.value = true;
|
||||
editId.value = row.id;
|
||||
dialogTitle.value = `修改往来单位「${row.name}」`;
|
||||
Object.assign(form, {
|
||||
name: row.name,
|
||||
type: Number(row.type),
|
||||
group: Number(row.group),
|
||||
saleType: row.saleType,
|
||||
lianxiren: row.lianxiren || '',
|
||||
phone: row.phone || '',
|
||||
address: row.address || '',
|
||||
pinyin: row.pinyin || ''
|
||||
});
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
|
||||
async function handleSave() {
|
||||
if (!form.name) {
|
||||
ElMessage.warning('请填写往来单位名称');
|
||||
return;
|
||||
}
|
||||
const payload = {
|
||||
name: form.name,
|
||||
type: form.type,
|
||||
group: form.group,
|
||||
saleType: form.saleType,
|
||||
lianxiren: form.lianxiren,
|
||||
phone: form.phone,
|
||||
address: form.address,
|
||||
pinyin: form.pinyin
|
||||
};
|
||||
try {
|
||||
if (isEdit.value) {
|
||||
await request.put(`/sys/gongyingshang/${editId.value}`, payload);
|
||||
ElMessage.success('修改成功');
|
||||
} else {
|
||||
await request.post('/sys/gongyingshang', payload);
|
||||
ElMessage.success('新增成功');
|
||||
}
|
||||
dialogVisible.value = false;
|
||||
loadList();
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
async function handleDelete(row) {
|
||||
await ElMessageBox.confirm(`确定删除「${row.name}」吗?`, '提示', { type: 'warning' });
|
||||
try {
|
||||
await request.delete(`/sys/gongyingshang/${row.id}`);
|
||||
ElMessage.success('删除成功');
|
||||
loadList();
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
onMounted(loadList);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>往来单位</span>
|
||||
<el-button type="primary" @click="openCreate">新增往来单位</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="search-bar">
|
||||
<el-select v-model="query.type" style="width: 120px" @change="handleSearch">
|
||||
<el-option v-for="t in typeOptions" :key="t.value" :label="t.label" :value="t.value" />
|
||||
</el-select>
|
||||
<el-select v-model="query.saleType" placeholder="销售类型(全部)" clearable style="width: 150px" @change="handleSearch">
|
||||
<el-option v-for="s in saleTypeOptions" :key="s.value" :label="s.label" :value="s.value" />
|
||||
</el-select>
|
||||
<el-input
|
||||
v-model="query.like"
|
||||
placeholder="名称 / 简拼 / 联系人 / 电话"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter="handleSearch"
|
||||
@clear="handleSearch"
|
||||
/>
|
||||
<el-button type="primary" @click="handleSearch">查询</el-button>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="loading" :data="list" border stripe>
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
<el-table-column prop="name" label="往来单位" min-width="220" />
|
||||
<el-table-column prop="grouname" label="分组" width="90" />
|
||||
<el-table-column prop="saleTypeName" label="销售类型" width="100" />
|
||||
<el-table-column prop="lianxiren" label="联系人" width="100" />
|
||||
<el-table-column prop="phone" label="电话" width="130" />
|
||||
<el-table-column prop="address" label="地址" min-width="180" show-overflow-tooltip />
|
||||
<el-table-column prop="pinyin" label="简拼" width="90" />
|
||||
<el-table-column label="操作" width="140" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button size="small" type="primary" link @click="openEdit(row)">编辑</el-button>
|
||||
<el-button size="small" type="danger" link @click="handleDelete(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-pagination
|
||||
v-model:current-page="query.pageIndex"
|
||||
v-model:page-size="query.pageSize"
|
||||
:total="total"
|
||||
layout="total, prev, pager, next"
|
||||
class="pager"
|
||||
@current-change="loadList"
|
||||
/>
|
||||
|
||||
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="520px">
|
||||
<el-form :model="form" label-width="100px">
|
||||
<el-form-item label="名称" required>
|
||||
<el-input v-model="form.name" placeholder="往来单位名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="业务类型" required>
|
||||
<el-select v-model="form.type" style="width: 100%" @change="handleTypeChange">
|
||||
<el-option v-for="t in typeOptions" :key="t.value" :label="t.label" :value="t.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="分组">
|
||||
<el-select v-model="form.group" style="width: 100%">
|
||||
<el-option v-for="g in groupOptions" :key="g.value" :label="g.label" :value="g.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="销售类型">
|
||||
<el-select v-model="form.saleType" style="width: 100%">
|
||||
<el-option v-for="s in saleTypeOptions" :key="s.value" :label="s.label" :value="s.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="联系人">
|
||||
<el-input v-model="form.lianxiren" />
|
||||
</el-form-item>
|
||||
<el-form-item label="电话">
|
||||
<el-input v-model="form.phone" />
|
||||
</el-form-item>
|
||||
<el-form-item label="地址">
|
||||
<el-input v-model="form.address" />
|
||||
</el-form-item>
|
||||
<el-form-item label="简拼">
|
||||
<el-input v-model="form.pinyin" placeholder="如 HD" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSave">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.search-bar {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.pager {
|
||||
margin-top: 12px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,335 @@
|
||||
<script setup>
|
||||
import { ref, reactive, computed, onMounted } from 'vue';
|
||||
import request from '../../../utils/request';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
|
||||
const allData = ref([]);
|
||||
const treeData = ref([]);
|
||||
const loading = ref(false);
|
||||
const selectedPath = ref('');
|
||||
const treeRef = ref(null);
|
||||
|
||||
const query = reactive({ keyword: '', pageIndex: 1, pageSize: 20 });
|
||||
const pagedList = ref([]);
|
||||
const filteredTotal = ref(0);
|
||||
|
||||
const dataTypeOptions = ['string', 'int', 'decimal', 'bool', 'json', 'array'];
|
||||
|
||||
const dialogVisible = ref(false);
|
||||
const dialogTitle = ref('新增配置项');
|
||||
const isEdit = ref(false);
|
||||
const editId = ref(null);
|
||||
const form = reactive({
|
||||
category: '',
|
||||
keyName: '',
|
||||
value: '',
|
||||
dataType: 'string',
|
||||
description: '',
|
||||
isActive: true
|
||||
});
|
||||
|
||||
// 按命名空间(反斜杠分层)构建树,并统计各节点(含子级)配置项数量
|
||||
function buildTree(list) {
|
||||
const roots = [];
|
||||
const map = {};
|
||||
const paths = [...new Set(list.map((x) => (x.category || '').trim()).filter(Boolean))].sort();
|
||||
|
||||
paths.forEach((path) => {
|
||||
const parts = path.split('\\');
|
||||
let cur = '';
|
||||
parts.forEach((part, i) => {
|
||||
cur = i === 0 ? part : `${cur}\\${part}`;
|
||||
if (!map[cur]) {
|
||||
const node = { id: cur, label: part, children: [] };
|
||||
map[cur] = node;
|
||||
if (i === 0) roots.push(node);
|
||||
else map[parts.slice(0, i).join('\\')].children.push(node);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const countOf = (path) =>
|
||||
list.filter((x) => x.category === path || (x.category || '').startsWith(`${path}\\`)).length;
|
||||
Object.values(map).forEach((node) => {
|
||||
node.label = `${node.label} (${countOf(node.id)})`;
|
||||
});
|
||||
|
||||
return roots;
|
||||
}
|
||||
|
||||
function applyFilter() {
|
||||
let data = allData.value;
|
||||
if (selectedPath.value) {
|
||||
data = data.filter(
|
||||
(x) => x.category === selectedPath.value || (x.category || '').startsWith(`${selectedPath.value}\\`)
|
||||
);
|
||||
}
|
||||
const kw = (query.keyword || '').trim();
|
||||
if (kw) {
|
||||
data = data.filter(
|
||||
(x) =>
|
||||
(x.keyName || '').includes(kw) ||
|
||||
(x.value || '').includes(kw) ||
|
||||
(x.description || '').includes(kw) ||
|
||||
(x.category || '').includes(kw)
|
||||
);
|
||||
}
|
||||
filteredTotal.value = data.length;
|
||||
const start = (query.pageIndex - 1) * query.pageSize;
|
||||
pagedList.value = data.slice(start, start + query.pageSize);
|
||||
}
|
||||
|
||||
async function loadAll() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await request.get('/sys/registry');
|
||||
allData.value = res.data || [];
|
||||
treeData.value = buildTree(allData.value);
|
||||
applyFilter();
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleNodeClick(node) {
|
||||
selectedPath.value = node.id;
|
||||
query.pageIndex = 1;
|
||||
applyFilter();
|
||||
}
|
||||
|
||||
function handleClearNode() {
|
||||
selectedPath.value = '';
|
||||
treeRef.value?.setCurrentKey(null);
|
||||
query.pageIndex = 1;
|
||||
applyFilter();
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
query.pageIndex = 1;
|
||||
applyFilter();
|
||||
}
|
||||
|
||||
function handlePageChange(page) {
|
||||
query.pageIndex = page;
|
||||
applyFilter();
|
||||
}
|
||||
|
||||
function openCreate() {
|
||||
isEdit.value = false;
|
||||
dialogTitle.value = '新增配置项';
|
||||
Object.assign(form, {
|
||||
category: selectedPath.value || '',
|
||||
keyName: '',
|
||||
value: '',
|
||||
dataType: 'string',
|
||||
description: '',
|
||||
isActive: true
|
||||
});
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
|
||||
function openEdit(row) {
|
||||
isEdit.value = true;
|
||||
editId.value = row.id;
|
||||
dialogTitle.value = '修改配置项';
|
||||
Object.assign(form, {
|
||||
category: row.category,
|
||||
keyName: row.keyName,
|
||||
value: row.value,
|
||||
dataType: row.dataType || 'string',
|
||||
description: row.description || '',
|
||||
isActive: row.isActive
|
||||
});
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
|
||||
async function handleSave() {
|
||||
if (!form.category) {
|
||||
ElMessage.warning('请填写命名空间');
|
||||
return;
|
||||
}
|
||||
if (!form.keyName) {
|
||||
ElMessage.warning('请填写键名');
|
||||
return;
|
||||
}
|
||||
const payload = {
|
||||
category: form.category.trim(),
|
||||
keyName: form.keyName.trim(),
|
||||
value: form.value ?? '',
|
||||
dataType: form.dataType,
|
||||
description: form.description || '',
|
||||
isActive: form.isActive
|
||||
};
|
||||
try {
|
||||
if (isEdit.value) {
|
||||
await request.put(`/sys/registry/${editId.value}`, payload);
|
||||
ElMessage.success('修改成功');
|
||||
} else {
|
||||
await request.post('/sys/registry', payload);
|
||||
ElMessage.success('保存成功');
|
||||
}
|
||||
dialogVisible.value = false;
|
||||
loadAll();
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
async function handleDelete(row) {
|
||||
await ElMessageBox.confirm(`确定删除配置项「${row.keyName}」吗?`, '提示', { type: 'warning' });
|
||||
try {
|
||||
await request.delete(`/sys/registry/${row.id}`);
|
||||
ElMessage.success('删除成功');
|
||||
loadAll();
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
const currentPathLabel = computed(() => selectedPath.value || '全部配置项');
|
||||
|
||||
onMounted(loadAll);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>注册表</span>
|
||||
<el-button type="primary" @click="openCreate">新增配置项</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="registry-body">
|
||||
<div class="tree-panel">
|
||||
<div class="tree-title">
|
||||
<span>命名空间</span>
|
||||
<el-button size="small" link type="primary" @click="handleClearNode">全部</el-button>
|
||||
</div>
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
:data="treeData"
|
||||
node-key="id"
|
||||
highlight-current
|
||||
default-expand-all
|
||||
:props="{ label: 'label', children: 'children' }"
|
||||
@node-click="handleNodeClick"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="table-panel">
|
||||
<div class="search-bar">
|
||||
<el-input
|
||||
v-model="query.keyword"
|
||||
placeholder="键名 / 值 / 描述 / 命名空间"
|
||||
clearable
|
||||
style="width: 260px"
|
||||
@keyup.enter="handleSearch"
|
||||
@clear="handleSearch"
|
||||
/>
|
||||
<el-button type="primary" @click="handleSearch">查询</el-button>
|
||||
<span class="path-tip">当前:{{ currentPathLabel }}</span>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="loading" :data="pagedList" border stripe>
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
<el-table-column prop="keyName" label="键名" min-width="150" />
|
||||
<el-table-column prop="value" label="值" min-width="220" show-overflow-tooltip />
|
||||
<el-table-column prop="dataType" label="数据类型" width="100" />
|
||||
<el-table-column prop="description" label="描述" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column label="是否启用" width="90">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.isActive ? 'success' : 'info'">{{ row.isActive ? '启用' : '禁用' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="category" label="命名空间" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column label="操作" width="140" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button size="small" type="primary" link @click="openEdit(row)">编辑</el-button>
|
||||
<el-button size="small" type="danger" link @click="handleDelete(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-pagination
|
||||
:current-page="query.pageIndex"
|
||||
:page-size="query.pageSize"
|
||||
:total="filteredTotal"
|
||||
layout="total, prev, pager, next"
|
||||
class="pager"
|
||||
@current-change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="520px">
|
||||
<el-form :model="form" label-width="100px">
|
||||
<el-form-item label="命名空间" required>
|
||||
<el-input v-model="form.category" placeholder="如:基础数据\工种" />
|
||||
</el-form-item>
|
||||
<el-form-item label="键名" required>
|
||||
<el-input v-model="form.keyName" placeholder="如:业务类型" />
|
||||
</el-form-item>
|
||||
<el-form-item label="值">
|
||||
<el-input v-model="form.value" type="textarea" :rows="3" placeholder="数组类型存逗号分隔字符串;json 类型存 JSON 文本" />
|
||||
</el-form-item>
|
||||
<el-form-item label="数据类型">
|
||||
<el-select v-model="form.dataType" style="width: 100%">
|
||||
<el-option v-for="t in dataTypeOptions" :key="t" :label="t" :value="t" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="描述">
|
||||
<el-input v-model="form.description" placeholder="配置说明" />
|
||||
</el-form-item>
|
||||
<el-form-item label="是否启用">
|
||||
<el-switch v-model="form.isActive" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSave">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.registry-body {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
.tree-panel {
|
||||
width: 240px;
|
||||
flex-shrink: 0;
|
||||
border-right: 1px solid var(--el-border-color-lighter);
|
||||
padding-right: 8px;
|
||||
max-height: 620px;
|
||||
overflow: auto;
|
||||
}
|
||||
.tree-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.table-panel {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.search-bar {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.path-tip {
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 12px;
|
||||
}
|
||||
.pager {
|
||||
margin-top: 12px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,335 @@
|
||||
<script setup>
|
||||
import { ref, reactive, nextTick, onMounted } from 'vue';
|
||||
import request from '../../../utils/request';
|
||||
import { buildDeptTree } from '../../../utils/dept';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
|
||||
const list = ref([]);
|
||||
const total = ref(0);
|
||||
const loading = ref(false);
|
||||
const query = reactive({ pageIndex: 1, pageSize: 20, keyword: '' });
|
||||
|
||||
// 下拉选项来自注册表「基础数据\工种」(业务类型 / 生产成本分类),未配置时用默认值兜底
|
||||
const bizTypeOptions = ref(['生产', '辅助']);
|
||||
const sccbFenleiOptions = ref(['无', '管理']);
|
||||
|
||||
async function loadRegistryOptions() {
|
||||
try {
|
||||
const load = async (keyName) => {
|
||||
const res = await request.get('/sys/registry/by-key', {
|
||||
params: { category: '基础数据\\工种', keyName }
|
||||
});
|
||||
return (res.data?.value || '')
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean);
|
||||
};
|
||||
const [biz, fenlei] = await Promise.all([load('业务类型'), load('生产成本分类')]);
|
||||
if (biz.length) bizTypeOptions.value = biz;
|
||||
if (fenlei.length) sccbFenleiOptions.value = fenlei;
|
||||
} catch (e) {
|
||||
// 注册表未配置时保持默认选项
|
||||
}
|
||||
}
|
||||
|
||||
// 新增/编辑
|
||||
const dialogVisible = ref(false);
|
||||
const dialogTitle = ref('新增工种');
|
||||
const isEdit = ref(false);
|
||||
const editId = ref(null);
|
||||
const form = reactive({ workName: '', sortOrder: 100, bizType: '生产', sccbFenlei: '无' });
|
||||
|
||||
// 绑定部门抽屉
|
||||
const deptDrawerVisible = ref(false);
|
||||
const deptTree = ref([]);
|
||||
const deptTreeRef = ref(null);
|
||||
const boundDeptIds = ref([]);
|
||||
|
||||
// 关联角色抽屉
|
||||
const roleDrawerVisible = ref(false);
|
||||
const roleList = ref([]);
|
||||
const boundRoleIds = ref([]);
|
||||
|
||||
const currentRow = ref(null);
|
||||
|
||||
async function loadList() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await request.get('/sys/worktype', { params: query });
|
||||
list.value = res.data.items || [];
|
||||
total.value = res.data.total || 0;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
query.pageIndex = 1;
|
||||
loadList();
|
||||
}
|
||||
|
||||
function openCreate() {
|
||||
isEdit.value = false;
|
||||
dialogTitle.value = '新增工种';
|
||||
Object.assign(form, { workName: '', sortOrder: 100, bizType: '生产', sccbFenlei: '无' });
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
|
||||
function openEdit(row) {
|
||||
isEdit.value = true;
|
||||
editId.value = row.id;
|
||||
dialogTitle.value = '修改工种';
|
||||
Object.assign(form, {
|
||||
workName: row.workName,
|
||||
sortOrder: row.sortOrder,
|
||||
bizType: row.bizType,
|
||||
sccbFenlei: row.sccbFenlei
|
||||
});
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
|
||||
async function handleSave() {
|
||||
if (!form.workName) {
|
||||
ElMessage.warning('请填写工种名称');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (isEdit.value) {
|
||||
await request.put(`/sys/worktype/${editId.value}`, form);
|
||||
ElMessage.success('修改成功');
|
||||
} else {
|
||||
await request.post('/sys/worktype', form);
|
||||
ElMessage.success('新增成功');
|
||||
}
|
||||
dialogVisible.value = false;
|
||||
loadList();
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
async function handleDelete(row) {
|
||||
await ElMessageBox.confirm(`确定删除工种「${row.workName}」吗?`, '提示', { type: 'warning' });
|
||||
try {
|
||||
await request.delete(`/sys/worktype/${row.id}`);
|
||||
ElMessage.success('删除成功');
|
||||
loadList();
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
async function handleActive(row) {
|
||||
try {
|
||||
await request.put(`/sys/worktype/${row.id}/active`, { active: !row.isActive });
|
||||
ElMessage.success('操作成功');
|
||||
loadList();
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// ==================== 绑定部门 ====================
|
||||
async function openDeptDrawer(row) {
|
||||
currentRow.value = row;
|
||||
deptDrawerVisible.value = true;
|
||||
try {
|
||||
if (!deptTree.value.length) {
|
||||
const deps = await request.get('/sys/department');
|
||||
deptTree.value = buildDeptTree(deps.data);
|
||||
}
|
||||
const res = await request.get(`/sys/worktype/${row.id}/departments`);
|
||||
boundDeptIds.value = (res.data || []).map((x) => String(x.departmentId));
|
||||
await nextTick();
|
||||
deptTreeRef.value?.setCheckedKeys(boundDeptIds.value);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
async function handleDeptCheck(node, { checkedKeys }) {
|
||||
if (!currentRow.value) return;
|
||||
const workTypeId = currentRow.value.id;
|
||||
const current = (checkedKeys || []).map(String);
|
||||
const existing = boundDeptIds.value.map(String);
|
||||
const added = current.filter((k) => !existing.includes(k));
|
||||
const removed = existing.filter((k) => !current.includes(k));
|
||||
if (!added.length && !removed.length) return;
|
||||
|
||||
try {
|
||||
for (const id of added) {
|
||||
await request.post(`/sys/worktype/${workTypeId}/departments`, { departmentId: id });
|
||||
}
|
||||
for (const id of removed) {
|
||||
await request.delete(`/sys/worktype/${workTypeId}/departments/${id}`);
|
||||
}
|
||||
boundDeptIds.value = current;
|
||||
ElMessage.success('绑定成功');
|
||||
loadList();
|
||||
} catch (e) {
|
||||
await nextTick();
|
||||
deptTreeRef.value?.setCheckedKeys(boundDeptIds.value);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 关联角色 ====================
|
||||
async function openRoleDrawer(row) {
|
||||
currentRow.value = row;
|
||||
roleDrawerVisible.value = true;
|
||||
try {
|
||||
if (!roleList.value.length) {
|
||||
const roles = await request.get('/sys/role');
|
||||
roleList.value = roles.data || [];
|
||||
}
|
||||
const res = await request.get(`/sys/worktype/${row.id}/roles`);
|
||||
boundRoleIds.value = (res.data || []).map((x) => String(x.roleId));
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
async function handleRoleChange(keys) {
|
||||
if (!currentRow.value) return;
|
||||
const workTypeId = currentRow.value.id;
|
||||
const current = (keys || []).map(String);
|
||||
const added = current.filter((k) => !boundRoleIds.value.includes(k));
|
||||
const removed = boundRoleIds.value.filter((k) => !current.includes(k));
|
||||
|
||||
try {
|
||||
for (const id of added) {
|
||||
await request.post(`/sys/worktype/${workTypeId}/roles`, { roleId: id });
|
||||
}
|
||||
for (const id of removed) {
|
||||
await request.delete(`/sys/worktype/${workTypeId}/roles/${id}`);
|
||||
}
|
||||
boundRoleIds.value = current;
|
||||
ElMessage.success('关联成功');
|
||||
} catch (e) {
|
||||
// 失败回滚勾选状态
|
||||
try {
|
||||
const res = await request.get(`/sys/worktype/${workTypeId}/roles`);
|
||||
boundRoleIds.value = (res.data || []).map((x) => String(x.roleId));
|
||||
} catch (err) {}
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadRegistryOptions();
|
||||
loadList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>工种管理</span>
|
||||
<el-button type="primary" @click="openCreate">新增工种</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="search-bar">
|
||||
<el-input
|
||||
v-model="query.keyword"
|
||||
placeholder="工种名称 / 业务类型 / 生产成本分类"
|
||||
clearable
|
||||
style="width: 280px"
|
||||
@keyup.enter="handleSearch"
|
||||
/>
|
||||
<el-button type="primary" @click="handleSearch">查询</el-button>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="loading" :data="list" border stripe>
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
<el-table-column prop="workName" label="工种名称" min-width="140" fixed="left" />
|
||||
<el-table-column prop="bizType" label="业务类型" width="100" />
|
||||
<el-table-column prop="departmentNames" label="所属部门" min-width="220" show-overflow-tooltip />
|
||||
<el-table-column prop="sccbFenlei" label="生产成本分类" width="120" />
|
||||
<el-table-column prop="sortOrder" label="排序" width="80" />
|
||||
<el-table-column label="状态" width="80">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.isActive ? 'success' : 'danger'">{{ row.isActive ? '启用' : '禁用' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="340" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button size="small" type="primary" link @click="openDeptDrawer(row)">绑定部门</el-button>
|
||||
<el-button size="small" type="primary" link @click="openRoleDrawer(row)">关联角色</el-button>
|
||||
<el-button size="small" type="primary" link @click="openEdit(row)">编辑</el-button>
|
||||
<el-button size="small" :type="row.isActive ? 'warning' : 'success'" link @click="handleActive(row)">
|
||||
{{ row.isActive ? '禁用' : '启用' }}
|
||||
</el-button>
|
||||
<el-button size="small" type="danger" link @click="handleDelete(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-pagination
|
||||
v-model:current-page="query.pageIndex"
|
||||
v-model:page-size="query.pageSize"
|
||||
:total="total"
|
||||
layout="total, prev, pager, next"
|
||||
class="pager"
|
||||
@current-change="loadList"
|
||||
/>
|
||||
|
||||
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="460px">
|
||||
<el-form :model="form" label-width="110px">
|
||||
<el-form-item label="工种名称" required>
|
||||
<el-input v-model="form.workName" placeholder="请输入工种名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="业务类型" required>
|
||||
<el-select v-model="form.bizType" filterable allow-create default-first-option style="width: 100%">
|
||||
<el-option v-for="v in bizTypeOptions" :key="v" :label="v" :value="v" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="生产成本分类" required>
|
||||
<el-select v-model="form.sccbFenlei" filterable allow-create default-first-option style="width: 100%">
|
||||
<el-option v-for="v in sccbFenleiOptions" :key="v" :label="v" :value="v" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="排序">
|
||||
<el-input-number v-model="form.sortOrder" :min="0" :step="10" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSave">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-drawer v-model="deptDrawerVisible" :title="`绑定部门 - ${currentRow?.workName || ''}`" size="420px">
|
||||
<el-alert type="info" :closable="false" show-icon title="勾选或取消勾选即时保存" style="margin-bottom: 12px" />
|
||||
<el-tree
|
||||
ref="deptTreeRef"
|
||||
:data="deptTree"
|
||||
show-checkbox
|
||||
check-strictly
|
||||
node-key="id"
|
||||
default-expand-all
|
||||
:props="{ label: 'name', children: 'children' }"
|
||||
@check="handleDeptCheck"
|
||||
/>
|
||||
</el-drawer>
|
||||
|
||||
<el-drawer v-model="roleDrawerVisible" :title="`关联角色 - ${currentRow?.workName || ''}`" size="420px">
|
||||
<el-alert type="info" :closable="false" show-icon title="勾选或取消勾选即时保存" style="margin-bottom: 12px" />
|
||||
<el-checkbox-group v-model="boundRoleIds" @change="handleRoleChange">
|
||||
<el-checkbox v-for="r in roleList" :key="r.id" :value="String(r.id)" class="role-item">
|
||||
{{ r.name }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-drawer>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.search-bar {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.pager {
|
||||
margin-top: 12px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.role-item {
|
||||
display: block;
|
||||
margin: 6px 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,530 @@
|
||||
<script setup>
|
||||
import { ref, reactive, nextTick, onMounted } from 'vue';
|
||||
import request from '../../../utils/request';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
|
||||
// 枚举(与后端 WuziBusiness_Type_Enum / Department_DutyType_Enum 一致)
|
||||
const businessTypeOptions = [
|
||||
{ label: '原料', value: 1 },
|
||||
{ label: '中间品', value: 2 },
|
||||
{ label: '生丝', value: 3 },
|
||||
{ label: '副产品', value: 4 },
|
||||
{ label: '五金', value: 5 }
|
||||
];
|
||||
const departmentTypeOptions = [
|
||||
{ label: '其他', value: 5 },
|
||||
{ label: '仓储', value: 10 },
|
||||
{ label: '选剥茧', value: 15 },
|
||||
{ label: '煮茧', value: 20 },
|
||||
{ label: '前缫', value: 25 },
|
||||
{ label: '后缫', value: 30 },
|
||||
{ label: '设备办', value: 35 },
|
||||
{ label: '副产品', value: 40 },
|
||||
{ label: '质检', value: 45 },
|
||||
{ label: '丝绵', value: 50 },
|
||||
{ label: '人力资源', value: 55 },
|
||||
{ label: '丝业中心', value: 100 },
|
||||
{ label: '前缫管理', value: 105 },
|
||||
{ label: '后缫检验', value: 110 },
|
||||
{ label: '后缫整理', value: 115 }
|
||||
];
|
||||
|
||||
const groups = ref([]);
|
||||
const treeData = ref([]);
|
||||
const treeRef = ref(null);
|
||||
const currentGroup = ref(null);
|
||||
const groupLoading = ref(false);
|
||||
|
||||
const list = ref([]);
|
||||
const total = ref(0);
|
||||
const loading = ref(false);
|
||||
const query = reactive({ keyword: '', pageIndex: 1, pageSize: 20 });
|
||||
|
||||
// 分组编辑弹窗
|
||||
const groupDialogVisible = ref(false);
|
||||
const groupDialogTitle = ref('新增一级分类');
|
||||
const isGroupEdit = ref(false);
|
||||
const groupForm = reactive({
|
||||
id: null,
|
||||
parentId: 0,
|
||||
code: '',
|
||||
name: '',
|
||||
businessType: 1,
|
||||
departmentType: 10,
|
||||
sortOrder: 1,
|
||||
description: ''
|
||||
});
|
||||
|
||||
// 物资编辑弹窗
|
||||
const muluDialogVisible = ref(false);
|
||||
const muluDialogTitle = ref('新增物资目录');
|
||||
const isMuluEdit = ref(false);
|
||||
const muluForm = reactive({
|
||||
id: null,
|
||||
groupId: null,
|
||||
code: '',
|
||||
name: '',
|
||||
unit: '',
|
||||
guige: '',
|
||||
xinghao: '',
|
||||
pinyinName: '',
|
||||
sortOrder: 1,
|
||||
isYujing: false,
|
||||
kucunyujing: 0,
|
||||
saleRate: 0
|
||||
});
|
||||
|
||||
function buildTree(items) {
|
||||
const map = {};
|
||||
const roots = [];
|
||||
(items || []).forEach((g) => {
|
||||
map[g.id] = { id: g.id, label: `${g.name}(${g.code})`, raw: g, children: [] };
|
||||
});
|
||||
(items || []).forEach((g) => {
|
||||
const node = map[g.id];
|
||||
if (g.parentId && map[g.parentId]) map[g.parentId].children.push(node);
|
||||
else roots.push(node);
|
||||
});
|
||||
return roots;
|
||||
}
|
||||
|
||||
async function loadGroups() {
|
||||
groupLoading.value = true;
|
||||
try {
|
||||
const res = await request.get('/sys/wuzigroup');
|
||||
groups.value = res.data || [];
|
||||
treeData.value = buildTree(groups.value);
|
||||
await nextTick();
|
||||
if (!currentGroup.value && groups.value.length) {
|
||||
selectGroupById(groups.value[0].id);
|
||||
} else if (currentGroup.value) {
|
||||
treeRef.value?.setCurrentKey(currentGroup.value.id);
|
||||
}
|
||||
} finally {
|
||||
groupLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function selectGroupById(id) {
|
||||
const hit = groups.value.find((x) => x.id === id);
|
||||
if (!hit) return;
|
||||
currentGroup.value = hit;
|
||||
treeRef.value?.setCurrentKey(id);
|
||||
query.pageIndex = 1;
|
||||
loadMulu();
|
||||
}
|
||||
|
||||
function handleNodeClick(node) {
|
||||
currentGroup.value = node.raw;
|
||||
query.pageIndex = 1;
|
||||
loadMulu();
|
||||
}
|
||||
|
||||
async function loadMulu() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await request.get('/sys/wuzimulu', {
|
||||
params: {
|
||||
groupId: currentGroup.value?.id || 0,
|
||||
keyword: query.keyword || undefined,
|
||||
pageIndex: query.pageIndex,
|
||||
pageSize: query.pageSize
|
||||
}
|
||||
});
|
||||
list.value = res.data.items || [];
|
||||
total.value = res.data.total || 0;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
query.pageIndex = 1;
|
||||
loadMulu();
|
||||
}
|
||||
|
||||
/* ==================== 物资分组 ==================== */
|
||||
async function openGroupCreate(parentId) {
|
||||
const parent = parentId ? groups.value.find((x) => x.id === parentId) : null;
|
||||
const code = await request
|
||||
.get('/sys/wuzigroup/new-code', { params: { parentId: parentId || 0 } })
|
||||
.then((res) => res.data)
|
||||
.catch(() => '');
|
||||
isGroupEdit.value = false;
|
||||
groupDialogTitle.value = parentId ? `新增「${parent?.name}」子级` : '新增一级分类';
|
||||
Object.assign(groupForm, {
|
||||
id: null,
|
||||
parentId: parentId || 0,
|
||||
code,
|
||||
name: '',
|
||||
businessType: parent ? parent.businessType : 1,
|
||||
departmentType: parent ? parent.departmentType : 10,
|
||||
sortOrder: Number(code) || 1,
|
||||
description: ''
|
||||
});
|
||||
groupDialogVisible.value = true;
|
||||
}
|
||||
|
||||
async function openGroupEdit() {
|
||||
if (!currentGroup.value) {
|
||||
ElMessage.warning('请选择需修改的分组');
|
||||
return;
|
||||
}
|
||||
const res = await request.get(`/sys/wuzigroup/${currentGroup.value.id}`);
|
||||
const g = res.data;
|
||||
isGroupEdit.value = true;
|
||||
groupDialogTitle.value = `修改「${g.name}」`;
|
||||
Object.assign(groupForm, {
|
||||
id: g.id,
|
||||
parentId: g.parentId,
|
||||
code: g.code,
|
||||
name: g.name,
|
||||
businessType: g.businessType,
|
||||
departmentType: g.departmentType,
|
||||
sortOrder: g.sortOrder,
|
||||
description: g.description || ''
|
||||
});
|
||||
groupDialogVisible.value = true;
|
||||
}
|
||||
|
||||
async function saveGroup() {
|
||||
if (!groupForm.name) {
|
||||
ElMessage.warning('请填写分组名称');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (isGroupEdit.value) {
|
||||
await request.put(`/sys/wuzigroup/${groupForm.id}`, {
|
||||
businessType: groupForm.businessType,
|
||||
departmentType: groupForm.departmentType,
|
||||
name: groupForm.name,
|
||||
sortOrder: Number(groupForm.sortOrder) || 0,
|
||||
description: groupForm.description
|
||||
});
|
||||
ElMessage.success('修改成功');
|
||||
} else {
|
||||
await request.post('/sys/wuzigroup', {
|
||||
parentId: groupForm.parentId,
|
||||
businessType: groupForm.businessType,
|
||||
departmentType: groupForm.departmentType,
|
||||
code: groupForm.code,
|
||||
name: groupForm.name,
|
||||
sortOrder: Number(groupForm.sortOrder) || 0,
|
||||
description: groupForm.description
|
||||
});
|
||||
ElMessage.success('新增成功');
|
||||
}
|
||||
groupDialogVisible.value = false;
|
||||
await loadGroups();
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
async function deleteGroup() {
|
||||
if (!currentGroup.value) {
|
||||
ElMessage.warning('请选择需删除的分组');
|
||||
return;
|
||||
}
|
||||
const group = currentGroup.value;
|
||||
await ElMessageBox.confirm(`确定删除分组「${group.name}」吗?`, '提示', { type: 'warning' });
|
||||
try {
|
||||
await request.delete(`/sys/wuzigroup/${group.id}`);
|
||||
ElMessage.success('删除成功');
|
||||
currentGroup.value = null;
|
||||
await loadGroups();
|
||||
if (!currentGroup.value && groups.value.length) selectGroupById(groups.value[0].id);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
/* ==================== 物资目录 ==================== */
|
||||
async function openMuluCreate() {
|
||||
if (!currentGroup.value) {
|
||||
ElMessage.warning('请先选择分组');
|
||||
return;
|
||||
}
|
||||
const code = await request
|
||||
.get('/sys/wuzimulu/new-code', { params: { groupId: currentGroup.value.id } })
|
||||
.then((res) => res.data)
|
||||
.catch(() => '');
|
||||
isMuluEdit.value = false;
|
||||
muluDialogTitle.value = '新增物资目录';
|
||||
Object.assign(muluForm, {
|
||||
id: null,
|
||||
groupId: currentGroup.value.id,
|
||||
code,
|
||||
name: '',
|
||||
unit: '',
|
||||
guige: '',
|
||||
xinghao: '',
|
||||
pinyinName: '',
|
||||
sortOrder: Number(code) || 1,
|
||||
isYujing: false,
|
||||
kucunyujing: 0,
|
||||
saleRate: 0
|
||||
});
|
||||
muluDialogVisible.value = true;
|
||||
}
|
||||
|
||||
async function openMuluEdit(row) {
|
||||
const res = await request.get(`/sys/wuzimulu/${row.id}`);
|
||||
const m = res.data;
|
||||
isMuluEdit.value = true;
|
||||
muluDialogTitle.value = `修改物资「${m.name}」`;
|
||||
Object.assign(muluForm, {
|
||||
id: m.id,
|
||||
groupId: m.groupId,
|
||||
code: m.code,
|
||||
name: m.name,
|
||||
unit: m.unit || '',
|
||||
guige: m.guige || '',
|
||||
xinghao: m.xinghao || '',
|
||||
pinyinName: m.pinyinName || '',
|
||||
sortOrder: m.sortOrder,
|
||||
isYujing: m.isYujing,
|
||||
kucunyujing: m.kucunyujing,
|
||||
saleRate: m.saleRate
|
||||
});
|
||||
muluDialogVisible.value = true;
|
||||
}
|
||||
|
||||
async function saveMulu() {
|
||||
if (!muluForm.name) {
|
||||
ElMessage.warning('请填写物资名称');
|
||||
return;
|
||||
}
|
||||
const payload = {
|
||||
groupId: muluForm.groupId,
|
||||
name: muluForm.name,
|
||||
code: muluForm.code,
|
||||
unit: muluForm.unit,
|
||||
guige: muluForm.guige,
|
||||
xinghao: muluForm.xinghao,
|
||||
pinyinName: muluForm.pinyinName,
|
||||
kucunyujing: Number(muluForm.kucunyujing) || 0,
|
||||
sortOrder: Number(muluForm.sortOrder) || 0,
|
||||
isYujing: muluForm.isYujing,
|
||||
saleRate: Number(muluForm.saleRate) || 0
|
||||
};
|
||||
try {
|
||||
if (isMuluEdit.value) {
|
||||
await request.put(`/sys/wuzimulu/${muluForm.id}`, payload);
|
||||
ElMessage.success('修改成功');
|
||||
} else {
|
||||
await request.post('/sys/wuzimulu', payload);
|
||||
ElMessage.success('新增成功');
|
||||
}
|
||||
muluDialogVisible.value = false;
|
||||
loadMulu();
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
async function deleteMulu(row) {
|
||||
await ElMessageBox.confirm(`确定删除物资「${row.name}」吗?`, '提示', { type: 'warning' });
|
||||
try {
|
||||
await request.delete(`/sys/wuzimulu/${row.id}`);
|
||||
ElMessage.success('删除成功');
|
||||
loadMulu();
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
onMounted(loadGroups);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>物资目录</span>
|
||||
<div>
|
||||
<el-button @click="openGroupCreate(0)">新增一级分类</el-button>
|
||||
<el-button :disabled="!currentGroup" @click="openGroupCreate(currentGroup?.id)">新增子级</el-button>
|
||||
<el-button :disabled="!currentGroup" @click="openGroupEdit">修改分组</el-button>
|
||||
<el-button :disabled="!currentGroup" type="danger" @click="deleteGroup">删除分组</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="wuzi-body">
|
||||
<div class="tree-panel" v-loading="groupLoading">
|
||||
<div class="tree-title">物资分组</div>
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
:data="treeData"
|
||||
node-key="id"
|
||||
highlight-current
|
||||
default-expand-all
|
||||
:props="{ label: 'label', children: 'children' }"
|
||||
@node-click="handleNodeClick"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="table-panel">
|
||||
<div class="search-bar">
|
||||
<span class="group-tip">当前分组:{{ currentGroup ? `${currentGroup.name}(${currentGroup.code})` : '未选择' }}</span>
|
||||
<el-input
|
||||
v-model="query.keyword"
|
||||
placeholder="名称 / 编号 / 拼音码"
|
||||
clearable
|
||||
style="width: 220px"
|
||||
@keyup.enter="handleSearch"
|
||||
@clear="handleSearch"
|
||||
/>
|
||||
<el-button type="primary" @click="handleSearch">查询</el-button>
|
||||
<el-button type="primary" :disabled="!currentGroup" @click="openMuluCreate">新增物资</el-button>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="loading" :data="list" border stripe>
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
<el-table-column prop="name" label="名称" min-width="160" />
|
||||
<el-table-column prop="code" label="编号" width="100" />
|
||||
<el-table-column prop="unit" label="单位" width="70" />
|
||||
<el-table-column prop="guige" label="规格" min-width="110" show-overflow-tooltip />
|
||||
<el-table-column prop="xinghao" label="型号" min-width="110" show-overflow-tooltip />
|
||||
<el-table-column prop="pinyinName" label="拼音码" width="90" />
|
||||
<el-table-column prop="saleRate" label="销售税率" width="90" />
|
||||
<el-table-column prop="sortOrder" label="顺序号" width="80" />
|
||||
<el-table-column label="启用预警" width="90">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.isYujing ? 'success' : 'info'">{{ row.isYujing ? '是' : '否' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="kucunyujing" label="预警数量" width="90" />
|
||||
<el-table-column label="操作" width="140" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button size="small" type="primary" link @click="openMuluEdit(row)">编辑</el-button>
|
||||
<el-button size="small" type="danger" link @click="deleteMulu(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-pagination
|
||||
v-model:current-page="query.pageIndex"
|
||||
v-model:page-size="query.pageSize"
|
||||
:total="total"
|
||||
layout="total, prev, pager, next"
|
||||
class="pager"
|
||||
@current-change="loadMulu"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分组编辑 -->
|
||||
<el-dialog v-model="groupDialogVisible" :title="groupDialogTitle" width="480px">
|
||||
<el-form :model="groupForm" label-width="110px">
|
||||
<el-form-item label="编号">
|
||||
<el-input v-model="groupForm.code" readonly placeholder="自动生成" />
|
||||
</el-form-item>
|
||||
<el-form-item label="名称" required>
|
||||
<el-input v-model="groupForm.name" placeholder="分组名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="业务类型">
|
||||
<el-select v-model="groupForm.businessType" style="width: 100%">
|
||||
<el-option v-for="b in businessTypeOptions" :key="b.value" :label="b.label" :value="b.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="部门职责">
|
||||
<el-select v-model="groupForm.departmentType" filterable style="width: 100%">
|
||||
<el-option v-for="d in departmentTypeOptions" :key="d.value" :label="d.label" :value="d.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="顺序号">
|
||||
<el-input-number v-model="groupForm.sortOrder" :min="0" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="描述">
|
||||
<el-input v-model="groupForm.description" type="textarea" :rows="2" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="groupDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="saveGroup">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 物资编辑 -->
|
||||
<el-dialog v-model="muluDialogVisible" :title="muluDialogTitle" width="560px">
|
||||
<el-form :model="muluForm" label-width="100px">
|
||||
<el-form-item label="所属分组">
|
||||
<el-select v-model="muluForm.groupId" filterable style="width: 100%">
|
||||
<el-option v-for="g in groups" :key="g.id" :label="`${g.name}(${g.code})`" :value="g.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="编号">
|
||||
<el-input v-model="muluForm.code" readonly placeholder="按分组自动生成" />
|
||||
</el-form-item>
|
||||
<el-form-item label="名称" required>
|
||||
<el-input v-model="muluForm.name" placeholder="物资名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="单位">
|
||||
<el-input v-model="muluForm.unit" placeholder="如 kg / 个 / 包" />
|
||||
</el-form-item>
|
||||
<el-form-item label="规格">
|
||||
<el-input v-model="muluForm.guige" />
|
||||
</el-form-item>
|
||||
<el-form-item label="型号">
|
||||
<el-input v-model="muluForm.xinghao" />
|
||||
</el-form-item>
|
||||
<el-form-item label="拼音码">
|
||||
<el-input v-model="muluForm.pinyinName" />
|
||||
</el-form-item>
|
||||
<el-form-item label="顺序号">
|
||||
<el-input-number v-model="muluForm.sortOrder" :min="0" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="销售税率">
|
||||
<el-input-number v-model="muluForm.saleRate" :min="0" :precision="2" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="启用预警">
|
||||
<el-switch v-model="muluForm.isYujing" />
|
||||
</el-form-item>
|
||||
<el-form-item label="预警数量">
|
||||
<el-input-number v-model="muluForm.kucunyujing" :min="0" :precision="2" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="muluDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="saveMulu">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.wuzi-body {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
.tree-panel {
|
||||
width: 250px;
|
||||
flex-shrink: 0;
|
||||
border-right: 1px solid var(--el-border-color-lighter);
|
||||
padding-right: 8px;
|
||||
max-height: 640px;
|
||||
overflow: auto;
|
||||
}
|
||||
.tree-title {
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.table-panel {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.search-bar {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.group-tip {
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 12px;
|
||||
flex: 1;
|
||||
}
|
||||
.pager {
|
||||
margin-top: 12px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,406 @@
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import request from '../../../utils/request';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
|
||||
const list = ref([]);
|
||||
const total = ref(0);
|
||||
const loading = ref(false);
|
||||
const query = reactive({
|
||||
year: String(new Date().getFullYear()),
|
||||
chandi: '',
|
||||
zhandian: '',
|
||||
jibie: '',
|
||||
like: '',
|
||||
pageIndex: 1,
|
||||
pageSize: 20
|
||||
});
|
||||
|
||||
// 注册表「基础数据\原料庄口」提供的下拉选项(允许手工录入新值)
|
||||
const registryOptions = reactive({ chandi: [], zhandian: [], jibie: [], canpinzhong: [] });
|
||||
const wuziOptions = ref([]);
|
||||
const bendiwaidiOptions = [
|
||||
{ label: '本地', value: 1 },
|
||||
{ label: '外地', value: 2 }
|
||||
];
|
||||
|
||||
const dialogVisible = ref(false);
|
||||
const dialogTitle = ref('新增原料庄口');
|
||||
const isEdit = ref(false);
|
||||
const editId = ref(null);
|
||||
const nameAuto = ref(true);
|
||||
|
||||
const form = reactive({
|
||||
year: String(new Date().getFullYear()),
|
||||
name: '',
|
||||
jibie: '',
|
||||
bendiwaidi: 1,
|
||||
chandi: '',
|
||||
canpinzhong: '',
|
||||
wzmlId: null,
|
||||
zhandian: '',
|
||||
pici: 0
|
||||
});
|
||||
|
||||
const digits = ['', '一', '二', '三', '四', '五', '六', '七', '八', '九'];
|
||||
|
||||
function bendiwaidiLabel(v) {
|
||||
const hit = bendiwaidiOptions.find((x) => x.value === Number(v));
|
||||
return hit ? hit.label : v;
|
||||
}
|
||||
|
||||
async function loadRegistryOptions() {
|
||||
const load = async (keyName) => {
|
||||
const res = await request.get('/sys/registry/by-key', {
|
||||
params: { category: '基础数据\\原料庄口', keyName }
|
||||
});
|
||||
return (res.data?.value || '')
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean);
|
||||
};
|
||||
try {
|
||||
const [chandi, zhandian, jibie, canpinzhong] = await Promise.all([
|
||||
load('产地'),
|
||||
load('站点'),
|
||||
load('季别'),
|
||||
load('蚕品种')
|
||||
]);
|
||||
registryOptions.chandi = chandi;
|
||||
registryOptions.zhandian = zhandian;
|
||||
registryOptions.jibie = jibie;
|
||||
registryOptions.canpinzhong = canpinzhong;
|
||||
} catch (e) {
|
||||
// 注册表未配置时下拉为空,仍可手工录入
|
||||
}
|
||||
}
|
||||
|
||||
async function loadWuziOptions() {
|
||||
try {
|
||||
const res = await request.get('/sys/yuanliaozhuangkou/wuzi-options');
|
||||
wuziOptions.value = res.data || [];
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
async function loadList() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await request.get('/sys/yuanliaozhuangkou', {
|
||||
params: {
|
||||
year: query.year || 0,
|
||||
chandi: query.chandi || undefined,
|
||||
zhandian: query.zhandian || undefined,
|
||||
jibie: query.jibie || undefined,
|
||||
like: query.like || undefined,
|
||||
pageIndex: query.pageIndex,
|
||||
pageSize: query.pageSize
|
||||
}
|
||||
});
|
||||
list.value = res.data.items || [];
|
||||
total.value = res.data.total || 0;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
query.pageIndex = 1;
|
||||
loadList();
|
||||
}
|
||||
|
||||
// 按 "{年度}{站点}{季别}{批次中文}{物资名去茧}({蚕品种})" 生成庄口名称
|
||||
function calcName() {
|
||||
if (!nameAuto.value) return;
|
||||
const yearStr = form.year || '';
|
||||
const zhandianStr = (form.zhandian || '').trim();
|
||||
const jibieStr = (form.jibie || '').trim();
|
||||
const pici = Number(form.pici) || 0;
|
||||
const piciStr = pici < digits.length ? digits[pici] : String(pici);
|
||||
const wuzi = wuziOptions.value.find((x) => x.id === form.wzmlId);
|
||||
const wzmlName = (wuzi?.name || '').replace(/茧/g, '');
|
||||
const canpinzhongStr = (form.canpinzhong || '').trim();
|
||||
if (!yearStr && !zhandianStr && !jibieStr && !wzmlName) return;
|
||||
form.name = `${yearStr}${zhandianStr}${jibieStr}${piciStr}${wzmlName}(${canpinzhongStr})`;
|
||||
}
|
||||
|
||||
function openCreate() {
|
||||
isEdit.value = false;
|
||||
nameAuto.value = true;
|
||||
dialogTitle.value = '新增原料庄口';
|
||||
Object.assign(form, {
|
||||
year: String(new Date().getFullYear()),
|
||||
name: '',
|
||||
jibie: '',
|
||||
bendiwaidi: 1,
|
||||
chandi: '',
|
||||
canpinzhong: '',
|
||||
wzmlId: wuziOptions.value.length === 1 ? wuziOptions.value[0].id : null,
|
||||
zhandian: '',
|
||||
pici: 0
|
||||
});
|
||||
dialogVisible.value = true;
|
||||
calcName();
|
||||
}
|
||||
|
||||
function openEdit(row) {
|
||||
isEdit.value = true;
|
||||
nameAuto.value = false;
|
||||
editId.value = row.id;
|
||||
dialogTitle.value = '修改原料庄口';
|
||||
Object.assign(form, {
|
||||
year: String(row.year),
|
||||
name: row.name,
|
||||
jibie: row.jibie,
|
||||
bendiwaidi: Number(row.bendiwaidi),
|
||||
chandi: row.chandi,
|
||||
canpinzhong: row.canpinzhong,
|
||||
wzmlId: row.wzmlId,
|
||||
zhandian: row.zhandian,
|
||||
pici: row.pici
|
||||
});
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
|
||||
async function handleSave() {
|
||||
if (!form.name) {
|
||||
ElMessage.warning('请填写庄口名称');
|
||||
return;
|
||||
}
|
||||
if (!form.chandi || !form.zhandian || !form.jibie || !form.canpinzhong) {
|
||||
ElMessage.warning('请填写产地、站点、季别、蚕品种');
|
||||
return;
|
||||
}
|
||||
if (!form.wzmlId) {
|
||||
ElMessage.warning('请选择茧别(物资)');
|
||||
return;
|
||||
}
|
||||
const payload = {
|
||||
year: Number(form.year),
|
||||
name: form.name,
|
||||
jibie: form.jibie,
|
||||
bendiWaidi: form.bendiwaidi,
|
||||
chandi: form.chandi,
|
||||
canpinzhong: form.canpinzhong,
|
||||
wzmlId: form.wzmlId,
|
||||
zhandian: form.zhandian,
|
||||
pici: Number(form.pici) || 0
|
||||
};
|
||||
try {
|
||||
if (isEdit.value) {
|
||||
await request.put(`/sys/yuanliaozhuangkou/${editId.value}`, payload);
|
||||
ElMessage.success('修改成功');
|
||||
} else {
|
||||
await request.post('/sys/yuanliaozhuangkou', payload);
|
||||
ElMessage.success('新增成功');
|
||||
}
|
||||
dialogVisible.value = false;
|
||||
loadList();
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
async function handleDelete(row) {
|
||||
await ElMessageBox.confirm(`确定删除原料庄口「${row.name}」吗?`, '提示', { type: 'warning' });
|
||||
try {
|
||||
await request.delete(`/sys/yuanliaozhuangkou/${row.id}`);
|
||||
ElMessage.success('删除成功');
|
||||
loadList();
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await Promise.all([loadRegistryOptions(), loadWuziOptions()]);
|
||||
loadList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>原料庄口</span>
|
||||
<el-button type="primary" @click="openCreate">新增原料庄口</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="search-bar">
|
||||
<el-date-picker
|
||||
v-model="query.year"
|
||||
type="year"
|
||||
placeholder="年度"
|
||||
value-format="YYYY"
|
||||
style="width: 130px"
|
||||
@change="handleSearch"
|
||||
/>
|
||||
<el-select v-model="query.chandi" placeholder="产地" clearable filterable style="width: 130px" @change="handleSearch">
|
||||
<el-option v-for="v in registryOptions.chandi" :key="v" :label="v" :value="v" />
|
||||
</el-select>
|
||||
<el-select v-model="query.zhandian" placeholder="站点" clearable filterable style="width: 130px" @change="handleSearch">
|
||||
<el-option v-for="v in registryOptions.zhandian" :key="v" :label="v" :value="v" />
|
||||
</el-select>
|
||||
<el-select v-model="query.jibie" placeholder="季别" clearable filterable style="width: 120px" @change="handleSearch">
|
||||
<el-option v-for="v in registryOptions.jibie" :key="v" :label="v" :value="v" />
|
||||
</el-select>
|
||||
<el-input
|
||||
v-model="query.like"
|
||||
placeholder="名称 / 编号 / 蚕品种"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
@keyup.enter="handleSearch"
|
||||
@clear="handleSearch"
|
||||
/>
|
||||
<el-button type="primary" @click="handleSearch">查询</el-button>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="loading" :data="list" border stripe>
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
<el-table-column prop="year" label="年度" width="80" />
|
||||
<el-table-column prop="name" label="庄口名称" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column prop="code" label="编号" width="100" />
|
||||
<el-table-column prop="wzmlName" label="茧别" width="90" />
|
||||
<el-table-column label="本地外地" width="90">
|
||||
<template #default="{ row }">{{ bendiwaidiLabel(row.bendiwaidi) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="chandi" label="产地" width="100" />
|
||||
<el-table-column prop="jibie" label="季别" width="90" />
|
||||
<el-table-column prop="canpinzhong" label="蚕品种" width="110" />
|
||||
<el-table-column prop="pici" label="批次" width="70" />
|
||||
<el-table-column prop="zhandian" label="站点" width="110" />
|
||||
<el-table-column label="操作" width="140" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button size="small" type="primary" link @click="openEdit(row)">编辑</el-button>
|
||||
<el-button size="small" type="danger" link @click="handleDelete(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-pagination
|
||||
v-model:current-page="query.pageIndex"
|
||||
v-model:page-size="query.pageSize"
|
||||
:total="total"
|
||||
layout="total, prev, pager, next"
|
||||
class="pager"
|
||||
@current-change="loadList"
|
||||
/>
|
||||
|
||||
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="560px">
|
||||
<el-form :model="form" label-width="100px">
|
||||
<el-form-item label="年度" required>
|
||||
<el-date-picker
|
||||
v-model="form.year"
|
||||
type="year"
|
||||
placeholder="年度"
|
||||
value-format="YYYY"
|
||||
style="width: 100%"
|
||||
@change="calcName"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="站点" required>
|
||||
<el-select
|
||||
v-model="form.zhandian"
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
placeholder="站点"
|
||||
style="width: 100%"
|
||||
@change="calcName"
|
||||
>
|
||||
<el-option v-for="v in registryOptions.zhandian" :key="v" :label="v" :value="v" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="季别" required>
|
||||
<el-select
|
||||
v-model="form.jibie"
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
placeholder="季别"
|
||||
style="width: 100%"
|
||||
@change="calcName"
|
||||
>
|
||||
<el-option v-for="v in registryOptions.jibie" :key="v" :label="v" :value="v" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="批次">
|
||||
<el-input-number v-model="form.pici" :min="0" :max="9" style="width: 100%" @change="calcName" />
|
||||
</el-form-item>
|
||||
<el-form-item label="茧别" required>
|
||||
<el-select
|
||||
v-model="form.wzmlId"
|
||||
filterable
|
||||
placeholder="选择物资目录中的茧类物资"
|
||||
style="width: 100%"
|
||||
@change="calcName"
|
||||
>
|
||||
<el-option
|
||||
v-for="w in wuziOptions"
|
||||
:key="w.id"
|
||||
:label="w.code ? `${w.name}(${w.code})` : w.name"
|
||||
:value="w.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="蚕品种" required>
|
||||
<el-select
|
||||
v-model="form.canpinzhong"
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
placeholder="蚕品种"
|
||||
style="width: 100%"
|
||||
@change="calcName"
|
||||
>
|
||||
<el-option v-for="v in registryOptions.canpinzhong" :key="v" :label="v" :value="v" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="产地" required>
|
||||
<el-select
|
||||
v-model="form.chandi"
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
placeholder="产地"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option v-for="v in registryOptions.chandi" :key="v" :label="v" :value="v" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="本地外地" required>
|
||||
<el-select v-model="form.bendiwaidi" style="width: 100%">
|
||||
<el-option v-for="b in bendiwaidiOptions" :key="b.value" :label="b.label" :value="b.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="庄口名称" required>
|
||||
<el-input v-model="form.name" placeholder="自动生成,可手工修改" @input="nameAuto = false" />
|
||||
<div class="form-tip">按「年度+站点+季别+批次+茧别(蚕品种)」自动生成</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSave">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.search-bar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.pager {
|
||||
margin-top: 12px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.form-tip {
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,256 @@
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import request from '../../../utils/request';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
|
||||
const list = ref([]);
|
||||
const total = ref(0);
|
||||
const loading = ref(false);
|
||||
const query = reactive({
|
||||
year: String(new Date().getFullYear()),
|
||||
keyword: '',
|
||||
pageIndex: 1,
|
||||
pageSize: 20
|
||||
});
|
||||
|
||||
const monthOptions = Array.from({ length: 12 }, (_, i) => i + 1);
|
||||
|
||||
// 新增/编辑
|
||||
const dialogVisible = ref(false);
|
||||
const dialogTitle = ref('新增账期');
|
||||
const isEdit = ref(false);
|
||||
const editId = ref(null);
|
||||
const form = reactive({
|
||||
year: String(new Date().getFullYear()),
|
||||
month: new Date().getMonth() + 1,
|
||||
startDate: '',
|
||||
endDate: ''
|
||||
});
|
||||
|
||||
function fmtDate(v) {
|
||||
return v ? String(v).slice(0, 10) : '';
|
||||
}
|
||||
|
||||
// 按 28 日边界推算默认账期区间(与 F9 一致)
|
||||
function defaultRange() {
|
||||
const now = new Date();
|
||||
let start;
|
||||
let end;
|
||||
if (now.getDate() <= 28) {
|
||||
start = new Date(now.getFullYear(), now.getMonth() - 1, 29);
|
||||
end = new Date(now.getFullYear(), now.getMonth(), 28);
|
||||
} else {
|
||||
start = new Date(now.getFullYear(), now.getMonth(), 29);
|
||||
end = new Date(now.getFullYear(), now.getMonth() + 1, 28);
|
||||
}
|
||||
const pad = (n) => String(n).padStart(2, '0');
|
||||
const str = (d) => `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
|
||||
return { start: str(start), end: str(end) };
|
||||
}
|
||||
|
||||
async function loadList() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await request.get('/sys/zhangqi', {
|
||||
params: {
|
||||
year: query.year || undefined,
|
||||
keyword: query.keyword || undefined,
|
||||
pageIndex: query.pageIndex,
|
||||
pageSize: query.pageSize
|
||||
}
|
||||
});
|
||||
list.value = res.data.items || [];
|
||||
total.value = res.data.total || 0;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
query.pageIndex = 1;
|
||||
loadList();
|
||||
}
|
||||
|
||||
function openCreate() {
|
||||
const range = defaultRange();
|
||||
isEdit.value = false;
|
||||
dialogTitle.value = '新增账期';
|
||||
Object.assign(form, {
|
||||
year: String(new Date().getFullYear()),
|
||||
month: new Date().getMonth() + 1,
|
||||
startDate: range.start,
|
||||
endDate: range.end
|
||||
});
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
|
||||
function openEdit(row) {
|
||||
isEdit.value = true;
|
||||
editId.value = row.id;
|
||||
dialogTitle.value = '修改账期';
|
||||
Object.assign(form, {
|
||||
year: String(row.year),
|
||||
month: row.month,
|
||||
startDate: fmtDate(row.startDate),
|
||||
endDate: fmtDate(row.endDate)
|
||||
});
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
|
||||
async function handleSave() {
|
||||
if (!form.startDate || !form.endDate) {
|
||||
ElMessage.warning('请选择开始日期与截止日期');
|
||||
return;
|
||||
}
|
||||
if (form.startDate > form.endDate) {
|
||||
ElMessage.warning('开始日期不能大于截止日期');
|
||||
return;
|
||||
}
|
||||
const payload = {
|
||||
year: Number(form.year),
|
||||
month: Number(form.month),
|
||||
startDate: `${form.startDate}T00:00:00`,
|
||||
endDate: `${form.endDate}T23:59:59`
|
||||
};
|
||||
try {
|
||||
if (isEdit.value) {
|
||||
await request.put(`/sys/zhangqi/${editId.value}`, payload);
|
||||
ElMessage.success('修改成功');
|
||||
} else {
|
||||
await request.post('/sys/zhangqi', payload);
|
||||
ElMessage.success('新增成功');
|
||||
}
|
||||
dialogVisible.value = false;
|
||||
loadList();
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
async function handleDelete(row) {
|
||||
await ElMessageBox.confirm(`确定删除 ${row.year} 年 ${row.month} 月账期吗?`, '提示', { type: 'warning' });
|
||||
try {
|
||||
await request.delete(`/sys/zhangqi/${row.id}`);
|
||||
ElMessage.success('删除成功');
|
||||
loadList();
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
async function handleZhaZhang(row) {
|
||||
const isZhaZhang = !row.isZhaZhang;
|
||||
const tip = isZhaZhang
|
||||
? '扎帐后,相关数据会禁止修改,是否继续?'
|
||||
: '取消扎帐后,相关数据允许修改,是否继续?';
|
||||
await ElMessageBox.confirm(tip, '提示', { type: 'warning' });
|
||||
try {
|
||||
await request.put(`/sys/zhangqi/${row.id}/zhazhang`, { isZhaZhang });
|
||||
ElMessage.success(isZhaZhang ? '扎帐成功' : '已取消扎帐');
|
||||
loadList();
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
onMounted(loadList);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>账期管理</span>
|
||||
<el-button type="primary" @click="openCreate">新增账期</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="search-bar">
|
||||
<el-date-picker
|
||||
v-model="query.year"
|
||||
type="year"
|
||||
placeholder="年度"
|
||||
value-format="YYYY"
|
||||
style="width: 140px"
|
||||
@change="handleSearch"
|
||||
/>
|
||||
<el-input
|
||||
v-model="query.keyword"
|
||||
placeholder="月份 / 年度"
|
||||
clearable
|
||||
style="width: 160px"
|
||||
@keyup.enter="handleSearch"
|
||||
/>
|
||||
<el-button type="primary" @click="handleSearch">查询</el-button>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="loading" :data="list" border stripe>
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
<el-table-column prop="year" label="年度" width="100" />
|
||||
<el-table-column prop="month" label="月份" width="100" />
|
||||
<el-table-column label="开始日期" width="140">
|
||||
<template #default="{ row }">{{ fmtDate(row.startDate) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="截止日期" width="140">
|
||||
<template #default="{ row }">{{ fmtDate(row.endDate) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="是否扎帐" min-width="120">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.isZhaZhang" type="success">已扎帐</el-tag>
|
||||
<el-tag v-else type="info">未扎帐</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="280" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button v-if="!row.isZhaZhang" size="small" type="primary" link @click="handleZhaZhang(row)">扎帐</el-button>
|
||||
<el-button v-else size="small" type="warning" link @click="handleZhaZhang(row)">取消扎帐</el-button>
|
||||
<el-button size="small" type="primary" link :disabled="row.isZhaZhang" @click="openEdit(row)">编辑</el-button>
|
||||
<el-button size="small" type="danger" link :disabled="row.isZhaZhang" @click="handleDelete(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-pagination
|
||||
v-model:current-page="query.pageIndex"
|
||||
v-model:page-size="query.pageSize"
|
||||
:total="total"
|
||||
layout="total, prev, pager, next"
|
||||
class="pager"
|
||||
@current-change="loadList"
|
||||
/>
|
||||
|
||||
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="480px">
|
||||
<el-form :model="form" label-width="100px">
|
||||
<el-form-item label="年度" required>
|
||||
<el-date-picker v-model="form.year" type="year" placeholder="年度" value-format="YYYY" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="月份" required>
|
||||
<el-select v-model="form.month" placeholder="月份" style="width: 100%">
|
||||
<el-option v-for="m in monthOptions" :key="m" :label="`${m} 月`" :value="m" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="开始日期" required>
|
||||
<el-date-picker v-model="form.startDate" type="date" placeholder="开始日期" value-format="YYYY-MM-DD" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="截止日期" required>
|
||||
<el-date-picker v-model="form.endDate" type="date" placeholder="截止日期" value-format="YYYY-MM-DD" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSave">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.search-bar {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.pager {
|
||||
margin-top: 12px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user