20260723225202
This commit is contained in:
1 parent
5f73b54802
commit
4d118a0037
22 files changed
+1403
-259
No files matched your search
@@ -0,0 +1,322 @@
|
||||
<script setup>
|
||||
import { computed, h, onMounted, ref } from "vue";
|
||||
import { message, Switch, Button, Popconfirm, Tag } from "antdv-next";
|
||||
import { Plus } from "@lucide/vue";
|
||||
import FmsTable from "@/components/fms-table/FmsTable.vue";
|
||||
import { loadDataApi, saveObjectApi, nextIdApi } from "@/services/api";
|
||||
import { usePermissionStore } from "@/stores/permissions";
|
||||
|
||||
const permissionStore = usePermissionStore();
|
||||
|
||||
const data = ref([]);
|
||||
const loading = ref(false);
|
||||
const saving = ref(false);
|
||||
|
||||
const canCreate = computed(() => permissionStore.canPower("m_i18n_type", "create"));
|
||||
const canUpdate = computed(() => permissionStore.canPower("m_i18n_type", "update"));
|
||||
const canDelete = computed(() => permissionStore.canPower("m_i18n_type", "delete"));
|
||||
|
||||
async function loadData() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await loadDataApi("b_i18n_type", "", "b_xh ASC, b_id ASC");
|
||||
data.value = res.data || [];
|
||||
} catch (e) {
|
||||
message.error("加载多语言类型失败:" + (e?.message || e));
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(loadData);
|
||||
|
||||
/* ---------- 行内单元格渲染 ---------- */
|
||||
function createDefaultCell() {
|
||||
return (props) =>
|
||||
props.row.b_default === 1
|
||||
? h(Tag, { color: "blue" }, "默认")
|
||||
: h("span", { class: "muted" }, "—");
|
||||
}
|
||||
|
||||
function createCanuseCell() {
|
||||
return (props) => {
|
||||
const row = props.row;
|
||||
return h(Switch, {
|
||||
checked: row.b_canuse === 1,
|
||||
size: "small",
|
||||
disabled: !canUpdate.value,
|
||||
"onUpdate:checked": (val) => {
|
||||
const next = val ? 1 : 0;
|
||||
if (row.b_canuse === next) return;
|
||||
row.b_canuse = next;
|
||||
saveRow(row, { b_canuse: next });
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function createActionsCell() {
|
||||
return (props) => {
|
||||
const row = props.row;
|
||||
const isDefault = row.b_default === 1;
|
||||
const children = [];
|
||||
if (canUpdate.value) {
|
||||
children.push(
|
||||
h(
|
||||
Button,
|
||||
{ type: "link", size: "small", onClick: (e) => { e.stopPropagation(); openEdit(row); } },
|
||||
"编辑",
|
||||
),
|
||||
);
|
||||
}
|
||||
if (canDelete.value && !isDefault) {
|
||||
children.push(
|
||||
h(
|
||||
Popconfirm,
|
||||
{
|
||||
title: "确定删除该语言?",
|
||||
okText: "删除",
|
||||
okButtonProps: { danger: true },
|
||||
onConfirm: () => removeRow(row),
|
||||
},
|
||||
{
|
||||
default: () =>
|
||||
h(
|
||||
Button,
|
||||
{ type: "link", size: "small", danger: true, onClick: (e) => e.stopPropagation() },
|
||||
"删除",
|
||||
),
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
return h("span", { class: "actions-cell" }, children);
|
||||
};
|
||||
}
|
||||
|
||||
const columns = [
|
||||
{ dataIndex: "b_code", title: "简称 / 编码", width: 150 },
|
||||
{ dataIndex: "b_name", title: "名称", width: 160 },
|
||||
{ dataIndex: "b_default", title: "默认", width: 80, align: "center", customCell: createDefaultCell() },
|
||||
{ dataIndex: "b_canuse", title: "是否启用", width: 100, align: "center", customCell: createCanuseCell() },
|
||||
{ dataIndex: "b_xh", title: "排序", width: 80, align: "center" },
|
||||
{ dataIndex: "actions", title: "操作", width: 130, align: "center", customCell: createActionsCell() },
|
||||
];
|
||||
|
||||
/* ---------- 行内开关即时保存 ---------- */
|
||||
async function saveRow(row, patch) {
|
||||
try {
|
||||
await saveObjectApi([
|
||||
{ table: "b_i18n_type", key_field: "b_id", updates: [{ b_id: row.b_id, ...patch }] },
|
||||
]);
|
||||
message.success("已更新");
|
||||
} catch (e) {
|
||||
message.error("更新失败:" + (e?.message || e));
|
||||
await loadData();
|
||||
}
|
||||
}
|
||||
|
||||
async function removeRow(row) {
|
||||
if (row.b_default === 1) return;
|
||||
saving.value = true;
|
||||
try {
|
||||
await saveObjectApi([
|
||||
{ table: "b_i18n_type", key_field: "b_id", deletes: [{ b_id: row.b_id }] },
|
||||
]);
|
||||
message.success("删除成功");
|
||||
await loadData();
|
||||
} catch (e) {
|
||||
message.error("删除失败:" + (e?.message || e));
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- 新增 / 编辑弹窗 ---------- */
|
||||
const modalVisible = ref(false);
|
||||
const modalMode = ref("create");
|
||||
const editingId = ref(null);
|
||||
const editingDefault = ref(false);
|
||||
const form = ref({
|
||||
b_code: "",
|
||||
b_name: "",
|
||||
b_xh: 0,
|
||||
defaultOn: false,
|
||||
canuseOn: true,
|
||||
});
|
||||
|
||||
function openCreate() {
|
||||
modalMode.value = "create";
|
||||
editingId.value = null;
|
||||
editingDefault.value = false;
|
||||
form.value = { b_code: "", b_name: "", b_xh: 0, defaultOn: false, canuseOn: true };
|
||||
modalVisible.value = true;
|
||||
}
|
||||
|
||||
function openEdit(row) {
|
||||
modalMode.value = "edit";
|
||||
editingId.value = row.b_id;
|
||||
editingDefault.value = row.b_default === 1;
|
||||
form.value = {
|
||||
b_code: row.b_code,
|
||||
b_name: row.b_name,
|
||||
b_xh: row.b_xh ?? 0,
|
||||
defaultOn: row.b_default === 1,
|
||||
canuseOn: row.b_canuse === 1,
|
||||
};
|
||||
modalVisible.value = true;
|
||||
}
|
||||
|
||||
async function saveForm() {
|
||||
const f = form.value;
|
||||
if (!f.b_code || !f.b_code.trim()) {
|
||||
message.warning("请填写语言编码");
|
||||
return;
|
||||
}
|
||||
if (!f.b_name || !f.b_name.trim()) {
|
||||
message.warning("请填写语言名称");
|
||||
return;
|
||||
}
|
||||
saving.value = true;
|
||||
try {
|
||||
const isNew = modalMode.value === "create" || !editingId.value;
|
||||
const b_id = isNew ? (await nextIdApi(1)).data : editingId.value;
|
||||
const row = {
|
||||
b_id,
|
||||
b_code: f.b_code.trim(),
|
||||
b_name: f.b_name.trim(),
|
||||
b_canuse: f.canuseOn ? 1 : 0,
|
||||
b_default: f.defaultOn ? 1 : 0,
|
||||
b_xh: f.b_xh ?? 0,
|
||||
};
|
||||
const req = { table: "b_i18n_type", key_field: "b_id", inserts: [], updates: [], deletes: [] };
|
||||
if (isNew) req.inserts.push(row);
|
||||
else req.updates.push(row);
|
||||
// 保证仅一个默认语言:若本次设为默认,则将其它语言取消默认
|
||||
if (row.b_default === 1) {
|
||||
for (const r of data.value) {
|
||||
if (r.b_id !== b_id && r.b_default === 1) {
|
||||
req.updates.push({ b_id: r.b_id, b_default: 0 });
|
||||
}
|
||||
}
|
||||
}
|
||||
await saveObjectApi([req]);
|
||||
message.success("保存成功");
|
||||
modalVisible.value = false;
|
||||
await loadData();
|
||||
} catch (e) {
|
||||
message.error("保存失败:" + (e?.message || e));
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="i18n-type-page">
|
||||
<div class="panel-card">
|
||||
<div class="panel-toolbar">
|
||||
<a-button v-if="canCreate" type="primary" @click="openCreate">
|
||||
<Plus :size="16" /> 新增语言
|
||||
</a-button>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<FmsTable
|
||||
:columns="columns"
|
||||
:data-source="data"
|
||||
row-key="b_id"
|
||||
:loading="loading"
|
||||
:pagination="false"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a-modal
|
||||
v-model:open="modalVisible"
|
||||
:title="modalMode === 'create' ? '新增语言' : '编辑语言'"
|
||||
:confirm-loading="saving"
|
||||
@ok="saveForm"
|
||||
@cancel="modalVisible = false"
|
||||
>
|
||||
<a-form layout="vertical">
|
||||
<a-form-item label="语言编码(简称)" required>
|
||||
<a-input
|
||||
v-model:value="form.b_code"
|
||||
:disabled="editingDefault"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="语言名称" required>
|
||||
<a-input v-model:value="form.b_name" />
|
||||
</a-form-item>
|
||||
<a-form-item label="是否默认">
|
||||
<a-switch v-model:checked="form.defaultOn" :disabled="editingDefault" />
|
||||
<span class="form-tip">设为默认后,其它语言将自动取消默认</span>
|
||||
</a-form-item>
|
||||
<a-form-item label="是否启用">
|
||||
<a-switch v-model:checked="form.canuseOn" />
|
||||
</a-form-item>
|
||||
<a-form-item label="排序">
|
||||
<a-input-number v-model:value="form.b_xh" :min="0" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.i18n-type-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.panel-card {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
flex-direction: column;
|
||||
background: #fff;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 1px 4px rgb(0 0 0 / 6%);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.panel-toolbar {
|
||||
display: flex;
|
||||
flex: none;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
&__title {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #1f2329;
|
||||
}
|
||||
}
|
||||
|
||||
.panel-body {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.actions-cell {
|
||||
display: inline-flex;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.muted {
|
||||
color: #c0c4cc;
|
||||
}
|
||||
|
||||
.form-tip {
|
||||
margin-left: 8px;
|
||||
font-size: 12px;
|
||||
color: #646a73;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user