20260728225443

This commit is contained in:
oneao committed 2026-07-28 22:54:43 +08:00
1 parent 5ec7aadef9
commit f6b79ea09a
30 files changed
+1541 -1120

No files matched your search

@@ -1,163 +1,143 @@
<script setup>
import { computed, h, onMounted, ref, shallowRef } 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";
import { computed, ref } from "vue";
import { message, Modal } from "antdv-next";
import { Plus, Trash2 } from "@lucide/vue";
import FmsModuleListPage from "@/components/fms-module-list/FmsModuleListPage.vue";
import { loadDataApi, nextIdApi, saveObjectApi } from "@/services/api";
import { useAppStore } from "@/stores/app";
const permissionStore = usePermissionStore();
const data = ref([]);
const loading = ref(false);
const appStore = useAppStore();
const listRef = ref(null);
const selectedRowKeys = ref([]);
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"));
const rowSelection = computed(() => {
return {
selectedRowKeys: selectedRowKeys.value,
getCheckboxProps: (row) => ({ disabled: Number(row.b_default) === 1 }),
onChange: (keys) => {
selectedRowKeys.value = keys;
},
};
});
async function loadData() {
loading.value = true;
const rowActions = computed(() => {
return [
{
key: "edit",
label: appStore.t("common.edit", "编辑"),
disabled: () => saving.value,
onClick: openEdit,
},
{
key: "delete",
type: "delete",
label: appStore.t("common.delete", "删除"),
confirm: appStore.t("common.confirmDeleteLanguage", "确定删除该语言?"),
disabled: ({ record }) => saving.value || Number(record.b_default) === 1,
onClick: removeRow,
},
];
});
async function reloadList() {
selectedRowKeys.value = [];
if (listRef.value) await listRef.value.reload();
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;
await appStore.loadLanguages();
} catch (error) {
message.error("刷新语言配置失败:" + (error?.message || error));
}
}
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 = shallowRef([
{ 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 getOtherDefaultUpdates(currentId) {
const response = await loadDataApi("b_i18n_type", "b_default = 1", "b_id ASC");
const updates = [];
for (const row of response.data || []) {
if (String(row.b_id) === String(currentId)) continue;
updates.push({ b_id: row.b_id, b_default: 0 });
}
return updates;
}
async function removeRow(row) {
if (row.b_default === 1) return;
async function updateCheckbox(row, field, checked) {
if (saving.value) return;
saving.value = true;
try {
const value = checked ? 1 : 0;
const updates = [{ b_id: row.b_id, [field]: value }];
if (field === "b_default" && value === 1) {
updates.push(...(await getOtherDefaultUpdates(row.b_id)));
}
await saveObjectApi([
{ table: "b_i18n_type", key_field: "b_id", deletes: [{ b_id: row.b_id }] },
{ table: "b_i18n_type", key_field: "b_id", inserts: [], updates, deletes: [] },
]);
message.success("删除成功");
await loadData();
} catch (e) {
message.error("删除失败:" + (e?.message || e));
message.success(appStore.t("common.updateSuccess", "更新成功"));
await reloadList();
} catch (error) {
message.error(appStore.t("common.updateFailed", "更新失败") + ":" + (error?.message || error));
await reloadList();
} finally {
saving.value = false;
}
}
/* ---------- 新增 / 编辑弹窗 ---------- */
async function removeRow(row) {
if (saving.value || Number(row.b_default) === 1) return;
saving.value = true;
try {
await saveObjectApi([
{
table: "b_i18n_type",
key_field: "b_id",
inserts: [],
updates: [],
deletes: [{ b_id: row.b_id }],
},
]);
message.success(appStore.t("common.deleteSuccess", "删除成功"));
await reloadList();
} catch (error) {
message.error(appStore.t("common.deleteFailed", "删除失败") + ":" + (error?.message || error));
} finally {
saving.value = false;
}
}
function batchDelete() {
if (saving.value || !selectedRowKeys.value.length) return;
const keys = [...selectedRowKeys.value];
Modal.confirm({
title: `${appStore.t("common.confirmBatchDelete", "确定删除选中的记录?")} (${keys.length})`,
okText: appStore.t("common.delete", "删除"),
okType: "danger",
cancelText: appStore.t("common.cancel", "取消"),
onOk: async () => {
saving.value = true;
try {
const deletes = keys.map((b_id) => ({ b_id }));
await saveObjectApi([
{
table: "b_i18n_type",
key_field: "b_id",
inserts: [],
updates: [],
deletes,
},
]);
message.success(appStore.t("common.deleteSuccess", "删除成功"));
await reloadList();
} catch (error) {
message.error(
appStore.t("common.deleteFailed", "删除失败") + ":" + (error?.message || error),
);
} finally {
saving.value = false;
}
},
});
}
const modalVisible = ref(false);
const modalMode = ref("create");
const editingId = ref(null);
@@ -174,63 +154,69 @@ function openCreate() {
modalMode.value = "create";
editingId.value = null;
editingDefault.value = false;
form.value = { b_code: "", b_name: "", b_xh: 0, defaultOn: false, canuseOn: true };
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;
editingDefault.value = Number(row.b_default) === 1;
form.value = {
b_code: row.b_code,
b_name: row.b_name,
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,
defaultOn: Number(row.b_default) === 1,
canuseOn: Number(row.b_canuse) === 1,
};
modalVisible.value = true;
}
async function saveForm() {
const f = form.value;
if (!f.b_code || !f.b_code.trim()) {
message.warning("请填写语言编码");
const current = form.value;
if (!String(current.b_code || "").trim()) {
message.warning(appStore.t("common.enterLanguageCode", "请填写语言编码"));
return;
}
if (!f.b_name || !f.b_name.trim()) {
message.warning("请填写语言名称");
if (!String(current.b_name || "").trim()) {
message.warning(appStore.t("common.enterLanguageName", "请填写语言名称"));
return;
}
saving.value = true;
try {
const isNew = modalMode.value === "create" || !editingId.value;
const b_id = isNew ? (await nextIdApi(1)).data : editingId.value;
const isNew = modalMode.value === "create";
const b_id = isNew ? (await nextIdApi()).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,
b_code: current.b_code.trim(),
b_name: current.b_name.trim(),
b_default: current.defaultOn ? 1 : 0,
b_canuse: current.canuseOn ? 1 : 0,
b_xh: current.b_xh ?? 0,
};
const request = {
table: "b_i18n_type",
key_field: "b_id",
inserts: isNew ? [row] : [],
updates: isNew ? [] : [row],
deletes: [],
};
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 });
}
}
request.updates.push(...(await getOtherDefaultUpdates(b_id)));
}
await saveObjectApi([req]);
message.success("保存成功");
await saveObjectApi([request]);
message.success(appStore.t("common.saveSuccess", "保存成功"));
modalVisible.value = false;
await loadData();
} catch (e) {
message.error("保存失败:" + (e?.message || e));
await reloadList();
} catch (error) {
message.error(appStore.t("common.saveFailed", "保存失败") + ":" + (error?.message || error));
} finally {
saving.value = false;
}
@@ -239,106 +225,90 @@ async function saveForm() {
<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
v-model:columns="columns"
:data-source="data"
row-key="b_id"
:loading="loading"
:pagination="false"
<FmsModuleListPage
ref="listRef"
:row-actions="rowActions"
:row-selection="rowSelection"
@loaded="selectedRowKeys = []"
>
<template #actions="{ loading }">
<a-space>
<a-button
type="primary"
:disabled="loading || saving"
@click="openCreate"
>
<Plus :size="16" />
{{ appStore.t("common.create", "新增") }}
</a-button>
<a-button
danger
:disabled="loading || saving || !selectedRowKeys.length"
@click="batchDelete"
>
<Trash2 :size="16" />
{{ appStore.t("common.batchDelete", "批量删除")
}}{{ selectedRowKeys.length ? `(${selectedRowKeys.length})` : "" }}
</a-button>
</a-space>
</template>
<template #cell-b_canuse="{ value, record }">
<a-checkbox
:checked="Number(value) === 1"
:disabled="saving"
@mousedown.stop
@click.stop
@update:checked="(checked) => updateCheckbox(record, 'b_canuse', checked)"
/>
</div>
</div>
</template>
<template #cell-b_default="{ value, record }">
<a-checkbox
:checked="Number(value) === 1"
:disabled="saving"
@mousedown.stop
@click.stop
@update:checked="(checked) => updateCheckbox(record, 'b_default', checked)"
/>
</template>
</FmsModuleListPage>
<a-modal
v-model:open="modalVisible"
:title="modalMode === 'create' ? '新增语言' : '编辑语言'"
:title="
modalMode === 'create'
? appStore.t('common.createLanguage', '新增语言')
: appStore.t('common.editLanguage', '编辑语言')
"
:confirm-loading="saving"
@ok="saveForm"
@cancel="modalVisible = false"
>
<a-form layout="vertical">
<a-form-item label="语言编码(简称)" required>
<a-form-item :label="appStore.t('common.languageCode', '语言编码')" required>
<a-input v-model:value="form.b_code" :disabled="editingDefault" />
</a-form-item>
<a-form-item label="语言名称" required>
<a-form-item :label="appStore.t('common.languageName', '语言名称')" 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 :label="appStore.t('common.defaultLanguage', '默认语言')">
<a-checkbox v-model:checked="form.defaultOn" />
</a-form-item>
<a-form-item label="是否启用">
<a-switch v-model:checked="form.canuseOn" />
<a-form-item :label="appStore.t('common.enabled', '启用')">
<a-checkbox 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 :label="appStore.t('common.sort', '排序')">
<a-input-number v-model:value="form.b_xh" style="width: 100%" />
</a-form-item>
</a-form>
</a-modal>
</div>
</template>
<style scoped lang="scss">
<style scoped>
.i18n-type-page {
display: flex;
flex-direction: column;
width: 100%;
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>