mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-29 23:44:41 +00:00
chore: 完成多批次代码优化与重构
- 重构工作流模块目录结构,迁移代码文件 - 修复类型断言空值安全问题,添加 ! 操作符 - 优化样式类名,替换 flex-cc 为标准 flex 工具类 - 更新路由标签简化文案,移除冗余注释 - 调整 ruff 配置,放宽行长度限制 - 更新 README 与多语言文案,优化项目描述 - 修复表单、图表组件的类型与样式问题 - 简化搜索表单、数据卡片的布局代码
This commit is contained in:
@@ -272,13 +272,13 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { h } from "vue";
|
||||
import { useAuth } from "@/hooks/core/useAuth";
|
||||
import { useTable } from "@/hooks/core/useTable";
|
||||
import { useTableSelection } from "@/hooks/core/useTableSelection";
|
||||
import { useCrudDialog } from "@/hooks/core/useCrudDialog";
|
||||
import { confirmDelete, confirmBatchDelete } from "@/hooks/core/useConfirm";
|
||||
import { renderTableOperationCell, type TableOperationAction } from "@utils";
|
||||
import type { ColumnOption } from "@/types/component";
|
||||
import { renderTableOperationCell, type TableOperationAction, resolveStatusColumns } from "@utils";
|
||||
import EmailAPI, {
|
||||
type EmailConfigTable,
|
||||
type EmailConfigCreateForm,
|
||||
@@ -290,9 +290,9 @@ import EmailAPI, {
|
||||
} from "@/api/module_platform/email";
|
||||
import type { SearchFormItem } from "@/components/forms/fa-search-bar/index.vue";
|
||||
import type { FormItem } from "@/components/forms/fa-form/index.vue";
|
||||
import FaSearchBar from "@/components/forms/fa-search-bar/index.vue";
|
||||
import FaForm from "@/components/forms/fa-form/index.vue";
|
||||
import { ElTag, ElMessage } from "element-plus";
|
||||
import type FaForm from "@/components/forms/fa-form/index.vue";
|
||||
import FaStatusTag from "@/components/others/fa-status-tag/index.vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
defineOptions({ name: "Email" });
|
||||
|
||||
@@ -343,7 +343,7 @@ const {
|
||||
core: {
|
||||
apiFn: EmailAPI.listConfig,
|
||||
apiParams: { page_no: 1, page_size: 10 },
|
||||
columnsFactory: (): ColumnOption<EmailConfigTable>[] => [
|
||||
columnsFactory: resolveStatusColumns<EmailConfigTable>(() => [
|
||||
{ type: "selection", width: 48, fixed: "left" },
|
||||
{ type: "globalIndex", width: 56, label: "序号" },
|
||||
{ prop: "name", label: "配置名称", minWidth: 140, showOverflowTooltip: true },
|
||||
@@ -355,30 +355,28 @@ const {
|
||||
prop: "use_tls",
|
||||
label: "SSL/TLS",
|
||||
width: 90,
|
||||
formatter: (row: EmailConfigTable) =>
|
||||
h(ElTag, { type: row.use_tls ? "success" : "info", size: "small" }, () =>
|
||||
row.use_tls ? "启用" : "禁用"
|
||||
),
|
||||
status: {
|
||||
true: { type: "success", text: "启用" },
|
||||
false: { type: "info", text: "禁用" },
|
||||
},
|
||||
},
|
||||
{
|
||||
prop: "is_default",
|
||||
label: "默认",
|
||||
width: 70,
|
||||
formatter: (row: EmailConfigTable) =>
|
||||
row.is_default
|
||||
? h(ElTag, { type: "warning", size: "small" }, () => "默认")
|
||||
: h("span", { class: "text-g-400" }, () => "—"),
|
||||
status: {
|
||||
true: { type: "warning", size: "small", text: "默认" },
|
||||
false: { type: "info", size: "small", text: "—" },
|
||||
},
|
||||
},
|
||||
{
|
||||
prop: "status",
|
||||
label: "状态",
|
||||
width: 70,
|
||||
formatter: (row: EmailConfigTable) =>
|
||||
h(
|
||||
ElTag,
|
||||
{ type: row.status === ("0" as any) ? "success" : "danger", size: "small" },
|
||||
() => (row.status === ("0" as any) ? "正常" : "禁用")
|
||||
),
|
||||
status: {
|
||||
"0": { type: "success", size: "small", text: "正常" },
|
||||
"1": { type: "danger", size: "small", text: "禁用" },
|
||||
},
|
||||
},
|
||||
{
|
||||
prop: "operation",
|
||||
@@ -413,7 +411,7 @@ const {
|
||||
return renderTableOperationCell(actions.filter((a) => a.perm != null && hasAuth(a.perm)));
|
||||
},
|
||||
},
|
||||
],
|
||||
]),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -469,7 +467,7 @@ const {
|
||||
core: {
|
||||
apiFn: EmailAPI.listTemplate,
|
||||
apiParams: { page_no: 1, page_size: 10 },
|
||||
columnsFactory: (): ColumnOption<EmailTemplateTable>[] => [
|
||||
columnsFactory: resolveStatusColumns<EmailTemplateTable>(() => [
|
||||
{ type: "selection", width: 48, fixed: "left" },
|
||||
{ type: "globalIndex", width: 56, label: "序号" },
|
||||
{ prop: "name", label: "模板名称", minWidth: 140, showOverflowTooltip: true },
|
||||
@@ -478,19 +476,17 @@ const {
|
||||
label: "模板编码",
|
||||
width: 140,
|
||||
formatter: (row: EmailTemplateTable) =>
|
||||
h(ElTag, { type: "info", size: "small" }, () => row.template_code),
|
||||
h(FaStatusTag, { type: "info", size: "small", label: row.template_code }),
|
||||
},
|
||||
{ prop: "subject", label: "邮件主题", minWidth: 200, showOverflowTooltip: true },
|
||||
{
|
||||
prop: "status",
|
||||
label: "状态",
|
||||
width: 70,
|
||||
formatter: (row: EmailTemplateTable) =>
|
||||
h(
|
||||
ElTag,
|
||||
{ type: row.status === ("0" as any) ? "success" : "danger", size: "small" },
|
||||
() => (row.status === ("0" as any) ? "正常" : "禁用")
|
||||
),
|
||||
status: {
|
||||
"0": { type: "success", size: "small", text: "正常" },
|
||||
"1": { type: "danger", size: "small", text: "禁用" },
|
||||
},
|
||||
},
|
||||
{ prop: "description", label: "备注", minWidth: 120, showOverflowTooltip: true },
|
||||
{
|
||||
@@ -519,7 +515,7 @@ const {
|
||||
return renderTableOperationCell(actions.filter((a) => a.perm != null && hasAuth(a.perm)));
|
||||
},
|
||||
},
|
||||
],
|
||||
]),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -569,7 +565,7 @@ const {
|
||||
core: {
|
||||
apiFn: EmailAPI.listLog,
|
||||
apiParams: { page_no: 1, page_size: 10 },
|
||||
columnsFactory: (): ColumnOption<EmailLogTable>[] => [
|
||||
columnsFactory: resolveStatusColumns<EmailLogTable>(() => [
|
||||
{ type: "globalIndex", width: 56, label: "序号" },
|
||||
{ prop: "to_email", label: "收件人", minWidth: 180, showOverflowTooltip: true },
|
||||
{ prop: "subject", label: "主题", minWidth: 200, showOverflowTooltip: true },
|
||||
@@ -579,7 +575,7 @@ const {
|
||||
width: 120,
|
||||
formatter: (row: EmailLogTable) =>
|
||||
row.template_code
|
||||
? h(ElTag, { type: "info", size: "small" }, () => row.template_code!)
|
||||
? h(FaStatusTag, { type: "info", size: "small", label: row.template_code! })
|
||||
: h("span", { class: "text-g-400" }, () => "—"),
|
||||
},
|
||||
{
|
||||
@@ -587,16 +583,16 @@ const {
|
||||
label: "业务类型",
|
||||
width: 110,
|
||||
formatter: (row: EmailLogTable) =>
|
||||
h(ElTag, { size: "small" }, () => bizTypeLabel(row.biz_type)),
|
||||
h(FaStatusTag, { size: "small", label: bizTypeLabel(row.biz_type) }),
|
||||
},
|
||||
{
|
||||
prop: "status",
|
||||
label: "发送状态",
|
||||
width: 90,
|
||||
formatter: (row: EmailLogTable) =>
|
||||
h(ElTag, { type: row.status === 1 ? "success" : "danger", size: "small" }, () =>
|
||||
row.status === 1 ? "成功" : "失败"
|
||||
),
|
||||
status: {
|
||||
1: { type: "success", text: "成功" },
|
||||
0: { type: "danger", text: "失败" },
|
||||
},
|
||||
},
|
||||
{ prop: "retry_count", label: "重试", width: 60 },
|
||||
{
|
||||
@@ -608,7 +604,7 @@ const {
|
||||
row.error_msg || h("span", { class: "text-g-400" }, () => "—"),
|
||||
},
|
||||
{ prop: "created_time", label: "发送时间", width: 168, showOverflowTooltip: true },
|
||||
],
|
||||
]),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1063,7 +1059,7 @@ async function handleTemplateBatchDelete() {
|
||||
// ══════════════════ 搜索逻辑 ════════════════════
|
||||
|
||||
async function handleConfigSearch(p: ConfigSearchForm) {
|
||||
replaceConfigSearchParams({ name: p.name || undefined } as any);
|
||||
replaceConfigSearchParams({ name: p.name || undefined } as unknown as ConfigSearchForm);
|
||||
getConfigData();
|
||||
}
|
||||
function onConfigResetSearch() {
|
||||
@@ -1075,7 +1071,7 @@ async function handleTemplateSearch(p: TemplateSearchForm) {
|
||||
replaceTemplateSearchParams({
|
||||
name: p.name || undefined,
|
||||
template_code: p.template_code || undefined,
|
||||
} as any);
|
||||
} as unknown as TemplateSearchForm);
|
||||
getTemplateData();
|
||||
}
|
||||
function onTemplateResetSearch() {
|
||||
@@ -1087,7 +1083,7 @@ async function handleLogSearch(p: LogSearchForm) {
|
||||
replaceLogSearchParams({
|
||||
to_email: p.to_email || undefined,
|
||||
template_code: p.template_code || undefined,
|
||||
} as any);
|
||||
} as unknown as LogSearchForm);
|
||||
getLogData();
|
||||
}
|
||||
function onLogResetSearch() {
|
||||
|
||||
@@ -84,16 +84,15 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, h } from "vue";
|
||||
import { ElMessageBox, ElButton, ElTabs, ElTabPane, ElTag } from "element-plus";
|
||||
import { ref, reactive, computed } from "vue";
|
||||
import { ElMessageBox, ElButton, ElTabs, ElTabPane } from "element-plus";
|
||||
import { useTable } from "@/hooks/core/useTable";
|
||||
import { useAuth } from "@/hooks/core/useAuth";
|
||||
import InvoiceAPI from "@/api/module_platform/invoice";
|
||||
import type { InvoiceTable } from "@/api/module_platform/invoice";
|
||||
import type { ColumnOption } from "@/types/component";
|
||||
import type { SearchFormItem } from "@/components/forms/fa-search-bar/index.vue";
|
||||
import type { FormItem } from "@/components/forms/fa-form/index.vue";
|
||||
import { renderTableOperationCell, type TableOperationAction } from "@utils";
|
||||
import { renderTableOperationCell, type TableOperationAction, resolveStatusColumns } from "@utils";
|
||||
|
||||
defineOptions({ name: "Invoice" });
|
||||
|
||||
@@ -103,18 +102,6 @@ const activeTab = ref("my");
|
||||
const isSuperAdmin = ref(true);
|
||||
const platformShowSearchBar = ref(true);
|
||||
|
||||
// ══════════════════ 标签函数 ════════════════════
|
||||
|
||||
function invoiceStatusLabel(status: number): string {
|
||||
const map: Record<number, string> = { 0: "待开具", 1: "已开具", 2: "开票失败", 3: "已作废" };
|
||||
return map[status] || String(status);
|
||||
}
|
||||
|
||||
function invoiceStatusType(status: number): string {
|
||||
const map: Record<number, string> = { 0: "warning", 1: "success", 2: "danger", 3: "info" };
|
||||
return map[status] || "info";
|
||||
}
|
||||
|
||||
// ══════════════════ 平台端:全部发票 ════════════════════
|
||||
|
||||
function buildPlatformRowActions(row: InvoiceTable): TableOperationAction[] {
|
||||
@@ -196,7 +183,7 @@ const {
|
||||
page_no: 1,
|
||||
page_size: 50,
|
||||
},
|
||||
columnsFactory: (): ColumnOption<InvoiceTable>[] => [
|
||||
columnsFactory: resolveStatusColumns<InvoiceTable>(() => [
|
||||
{ prop: "id", label: "ID", width: 60 },
|
||||
{ prop: "tenant_id", label: "租户ID", width: 80 },
|
||||
{ prop: "invoice_no", label: "发票号", width: 180, showOverflowTooltip: true },
|
||||
@@ -217,11 +204,11 @@ const {
|
||||
prop: "status",
|
||||
label: "状态",
|
||||
width: 100,
|
||||
formatter: (row) => {
|
||||
const t = invoiceStatusType(row.status) as "warning" | "success" | "info" | "danger";
|
||||
return h(ElTag, { type: t, size: "small" as const }, () =>
|
||||
invoiceStatusLabel(row.status)
|
||||
);
|
||||
status: {
|
||||
0: { type: "warning", text: "待开具" },
|
||||
1: { type: "success", text: "已开具" },
|
||||
2: { type: "danger", text: "开票失败" },
|
||||
3: { type: "info", text: "已作废" },
|
||||
},
|
||||
},
|
||||
{ prop: "address_info", label: "地址电话", width: 180, showOverflowTooltip: true },
|
||||
@@ -233,7 +220,7 @@ const {
|
||||
align: "center",
|
||||
formatter: (row) => formatPlatformOpCell(row),
|
||||
},
|
||||
],
|
||||
]),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -288,7 +275,7 @@ const {
|
||||
page_no: 1,
|
||||
page_size: 50,
|
||||
},
|
||||
columnsFactory: (): ColumnOption<InvoiceTable>[] => [
|
||||
columnsFactory: resolveStatusColumns<InvoiceTable>(() => [
|
||||
{ prop: "id", label: "ID", width: 60 },
|
||||
{ prop: "invoice_no", label: "发票号", width: 180, showOverflowTooltip: true },
|
||||
{ prop: "title", label: "抬头", minWidth: 180, showOverflowTooltip: true },
|
||||
@@ -308,11 +295,11 @@ const {
|
||||
prop: "status",
|
||||
label: "状态",
|
||||
width: 100,
|
||||
formatter: (row) => {
|
||||
const t = invoiceStatusType(row.status) as "warning" | "success" | "info" | "danger";
|
||||
return h(ElTag, { type: t, size: "small" as const }, () =>
|
||||
invoiceStatusLabel(row.status)
|
||||
);
|
||||
status: {
|
||||
0: { type: "warning", text: "待开具" },
|
||||
1: { type: "success", text: "已开具" },
|
||||
2: { type: "danger", text: "开票失败" },
|
||||
3: { type: "info", text: "已作废" },
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -322,7 +309,7 @@ const {
|
||||
showOverflowTooltip: true,
|
||||
formatter: (row) => row.created_time || "—",
|
||||
},
|
||||
],
|
||||
]),
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -81,15 +81,19 @@
|
||||
:scrollbar="false"
|
||||
>
|
||||
<template #type="{ row }">
|
||||
<ElTag v-if="row?.type === MenuTypeEnum.CATALOG" type="warning">目录</ElTag>
|
||||
<ElTag v-if="row?.type === MenuTypeEnum.MENU" type="success">菜单</ElTag>
|
||||
<ElTag v-if="row?.type === MenuTypeEnum.BUTTON" type="danger">按钮</ElTag>
|
||||
<ElTag v-if="row?.type === MenuTypeEnum.EXTLINK" type="info">外链</ElTag>
|
||||
<FaStatusTag v-if="row?.type === MenuTypeEnum.CATALOG" type="warning" label="目录" />
|
||||
<FaStatusTag v-if="row?.type === MenuTypeEnum.MENU" type="success" label="菜单" />
|
||||
<FaStatusTag v-if="row?.type === MenuTypeEnum.BUTTON" type="danger" label="按钮" />
|
||||
<FaStatusTag v-if="row?.type === MenuTypeEnum.EXTLINK" type="info" label="外链" />
|
||||
</template>
|
||||
<template #client="{ row }">
|
||||
<ElTag v-if="row?.client === MenuClientEnum.PC" type="primary">PC</ElTag>
|
||||
<ElTag v-else-if="row?.client === MenuClientEnum.APP" type="success">APP</ElTag>
|
||||
<ElTag v-else type="info">{{ row?.client || "—" }}</ElTag>
|
||||
<FaStatusTag v-if="row?.client === MenuClientEnum.PC" type="primary" label="PC" />
|
||||
<FaStatusTag
|
||||
v-else-if="row?.client === MenuClientEnum.APP"
|
||||
type="success"
|
||||
label="APP"
|
||||
/>
|
||||
<FaStatusTag v-else type="info" :label="(row as unknown as MenuTable)?.client || '—'" />
|
||||
</template>
|
||||
<template #icon="{ row }">
|
||||
<template v-if="row?.icon">
|
||||
@@ -332,6 +336,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { h } from "vue";
|
||||
defineOptions({
|
||||
name: "SysMenu",
|
||||
inheritAttrs: false,
|
||||
@@ -349,13 +354,13 @@ import MenuAPI, {
|
||||
import { MenuClientEnum, MenuTypeEnum } from "@/enums/system/menu.enum";
|
||||
import { formatTree } from "@utils/common";
|
||||
import { useAuth } from "@/hooks/core/useAuth";
|
||||
import { renderTableOperationCell, type TableOperationAction } from "@utils";
|
||||
import { renderTableOperationCell, type TableOperationAction, resolveStatusColumns } from "@utils";
|
||||
import type { SearchFormItem } from "@/components/forms/fa-search-bar/index.vue";
|
||||
import type FaSearchBar from "@/components/forms/fa-search-bar/index.vue";
|
||||
import type { FormItem } from "@/components/forms/fa-form/index.vue";
|
||||
import FaSearchBar from "@/components/forms/fa-search-bar/index.vue";
|
||||
import FaForm from "@/components/forms/fa-form/index.vue";
|
||||
import FaMenuRouteIcon from "@/components/others/fa-menu-routeIcon/index.vue";
|
||||
import { ElTag, ElMessage, ElMessageBox } from "element-plus";
|
||||
import type FaForm from "@/components/forms/fa-form/index.vue";
|
||||
import FaMenuRouteIcon from "@/components/others/fa-menu-route-icon/index.vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
|
||||
const { hasAuth } = useAuth();
|
||||
const appStore = useAppStore();
|
||||
@@ -363,7 +368,7 @@ const userStore = useUserStore();
|
||||
|
||||
type MenuSearchForm = {
|
||||
name?: string;
|
||||
status?: string;
|
||||
status?: number;
|
||||
created_time?: string[];
|
||||
};
|
||||
|
||||
@@ -376,25 +381,6 @@ function buildMenuListQuery(p: MenuSearchForm): MenuPageQuery {
|
||||
};
|
||||
}
|
||||
|
||||
function menuTypeTag(row: MenuTable) {
|
||||
const t = row.type;
|
||||
if (t === MenuTypeEnum.CATALOG) return h(ElTag, { type: "warning" }, () => "目录");
|
||||
if (t === MenuTypeEnum.MENU) return h(ElTag, { type: "success" }, () => "菜单");
|
||||
if (t === MenuTypeEnum.BUTTON) return h(ElTag, { type: "danger" }, () => "按钮");
|
||||
if (t === MenuTypeEnum.EXTLINK) return h(ElTag, { type: "info" }, () => "外链");
|
||||
return h("span", "—");
|
||||
}
|
||||
|
||||
function menuClientTag(row: MenuTable) {
|
||||
if (row.client === MenuClientEnum.PC) return h(ElTag, { type: "primary" }, () => "PC");
|
||||
if (row.client === MenuClientEnum.APP) return h(ElTag, { type: "success" }, () => "APP");
|
||||
return h(ElTag, { type: "info" }, () => String(row.client ?? "—"));
|
||||
}
|
||||
|
||||
function ynTag(v: boolean | undefined, yes = "是", no = "否") {
|
||||
return h(ElTag, { type: v ? "success" : "danger" }, () => (v ? yes : no));
|
||||
}
|
||||
|
||||
function buildMenuRowActions(
|
||||
row: MenuTable,
|
||||
ctx: {
|
||||
@@ -885,118 +871,159 @@ const opCtx = {
|
||||
onDelete: deleteMenuRow,
|
||||
};
|
||||
|
||||
const { columnChecks, columns } = useTableColumns<MenuTable>(() => [
|
||||
{ type: "selection", width: 48, fixed: "left" },
|
||||
{ type: "index", label: "序号", width: 60, fixed: "left" },
|
||||
{ prop: "name", label: "菜单名称", minWidth: 200, showOverflowTooltip: true },
|
||||
{
|
||||
prop: "icon",
|
||||
label: "图标",
|
||||
width: 72,
|
||||
align: "center",
|
||||
formatter: (row: MenuTable) =>
|
||||
row.icon
|
||||
? h(FaMenuRouteIcon, { icon: row.icon, style: { verticalAlign: "-0.15em" } })
|
||||
: h("span", { class: "text-g-400" }, "—"),
|
||||
},
|
||||
{
|
||||
prop: "status",
|
||||
label: "状态",
|
||||
width: 88,
|
||||
formatter: (row: MenuTable) =>
|
||||
h(ElTag, { type: row.status === 0 ? "success" : "danger" }, () =>
|
||||
row.status === 0 ? "启用" : "停用"
|
||||
),
|
||||
},
|
||||
{ prop: "type", label: "类型", width: 88, align: "center", formatter: menuTypeTag },
|
||||
{ prop: "client", label: "终端", width: 88, align: "center", formatter: menuClientTag },
|
||||
{
|
||||
prop: "scope",
|
||||
label: "可见范围",
|
||||
width: 96,
|
||||
align: "center",
|
||||
formatter: (row: MenuTable) =>
|
||||
row.scope
|
||||
? h(ElTag, { type: row.scope === "platform" ? "warning" : "success" }, () =>
|
||||
row.scope === "platform" ? "仅平台" : "租户可用"
|
||||
)
|
||||
: h("span", { class: "text-g-400" }, "—"),
|
||||
},
|
||||
{ prop: "order", label: "排序", width: 80 },
|
||||
{ prop: "route_name", label: "路由名称", minWidth: 100, showOverflowTooltip: true },
|
||||
{ prop: "route_path", label: "路由路径", minWidth: 140, showOverflowTooltip: true },
|
||||
{ prop: "permission", label: "权限标识", minWidth: 160, showOverflowTooltip: true },
|
||||
{ prop: "component_path", label: "组件路径", minWidth: 140, showOverflowTooltip: true },
|
||||
{ prop: "active_path", label: "激活路径", minWidth: 100, showOverflowTooltip: true },
|
||||
{ prop: "redirect", label: "重定向", minWidth: 100, showOverflowTooltip: true },
|
||||
{ prop: "link", label: "外链地址", minWidth: 140, showOverflowTooltip: true },
|
||||
{
|
||||
prop: "keep_alive",
|
||||
label: "是否缓存",
|
||||
width: 96,
|
||||
formatter: (row: MenuTable) => ynTag(row.keep_alive),
|
||||
},
|
||||
{
|
||||
prop: "hidden",
|
||||
label: "是否隐藏",
|
||||
width: 96,
|
||||
formatter: (row: MenuTable) => ynTag(row.hidden),
|
||||
},
|
||||
{
|
||||
prop: "always_show",
|
||||
label: "显示根路由",
|
||||
width: 108,
|
||||
formatter: (row: MenuTable) => ynTag(row.always_show),
|
||||
},
|
||||
{
|
||||
prop: "affix",
|
||||
label: "固定路由",
|
||||
width: 96,
|
||||
formatter: (row: MenuTable) => ynTag(row.affix),
|
||||
},
|
||||
{ prop: "title", label: "菜单标题", minWidth: 100, showOverflowTooltip: true },
|
||||
{
|
||||
prop: "is_iframe",
|
||||
label: "嵌入iframe",
|
||||
width: 100,
|
||||
formatter: (row: MenuTable) => ynTag(row.is_iframe),
|
||||
},
|
||||
{
|
||||
prop: "is_hide_tab",
|
||||
label: "隐藏标签页",
|
||||
width: 100,
|
||||
formatter: (row: MenuTable) => ynTag(row.is_hide_tab),
|
||||
},
|
||||
{
|
||||
prop: "show_badge",
|
||||
label: "红点角标",
|
||||
width: 96,
|
||||
formatter: (row: MenuTable) => ynTag(row.show_badge),
|
||||
},
|
||||
{ prop: "show_text_badge", label: "文字角标", width: 100, showOverflowTooltip: true },
|
||||
{
|
||||
prop: "params",
|
||||
label: "路由参数",
|
||||
minWidth: 100,
|
||||
formatter: (row: MenuTable) =>
|
||||
row.params == null
|
||||
? "—"
|
||||
: typeof row.params === "object"
|
||||
? JSON.stringify(row.params)
|
||||
: String(row.params),
|
||||
},
|
||||
{ prop: "description", label: "描述", minWidth: 140, showOverflowTooltip: true },
|
||||
{ prop: "created_time", label: "创建时间", width: 168, showOverflowTooltip: true },
|
||||
{ prop: "updated_time", label: "更新时间", width: 168, showOverflowTooltip: true },
|
||||
{
|
||||
prop: "operation",
|
||||
label: "操作",
|
||||
width: 220,
|
||||
fixed: "right",
|
||||
align: "right",
|
||||
formatter: (row: MenuTable) => formatMenuOperationCell(row, opCtx),
|
||||
},
|
||||
]);
|
||||
const { columnChecks, columns } = useTableColumns<MenuTable>(
|
||||
resolveStatusColumns(() => [
|
||||
{ type: "selection", width: 48, fixed: "left" },
|
||||
{ type: "index", label: "序号", width: 60, fixed: "left" },
|
||||
{ prop: "name", label: "菜单名称", minWidth: 200, showOverflowTooltip: true },
|
||||
{
|
||||
prop: "icon",
|
||||
label: "图标",
|
||||
width: 72,
|
||||
align: "center",
|
||||
formatter: (row: MenuTable) =>
|
||||
row.icon
|
||||
? h(FaMenuRouteIcon, { icon: row.icon, style: { verticalAlign: "-0.15em" } })
|
||||
: h("span", { class: "text-g-400" }, "—"),
|
||||
},
|
||||
{
|
||||
prop: "status",
|
||||
label: "状态",
|
||||
width: 88,
|
||||
status: {
|
||||
0: { type: "success", text: "启用" },
|
||||
1: { type: "danger", text: "停用" },
|
||||
},
|
||||
},
|
||||
{
|
||||
prop: "type",
|
||||
label: "类型",
|
||||
width: 88,
|
||||
align: "center",
|
||||
status: {
|
||||
1: { type: "warning", text: "目录" },
|
||||
2: { type: "success", text: "菜单" },
|
||||
3: { type: "danger", text: "按钮" },
|
||||
4: { type: "info", text: "外链" },
|
||||
},
|
||||
},
|
||||
{
|
||||
prop: "client",
|
||||
label: "终端",
|
||||
width: 88,
|
||||
align: "center",
|
||||
status: {
|
||||
pc: { type: "primary", text: "PC" },
|
||||
app: { type: "success", text: "APP" },
|
||||
},
|
||||
},
|
||||
{
|
||||
prop: "scope",
|
||||
label: "可见范围",
|
||||
width: 96,
|
||||
align: "center",
|
||||
status: {
|
||||
platform: { type: "warning", text: "仅平台" },
|
||||
tenant: { type: "success", text: "租户可用" },
|
||||
},
|
||||
},
|
||||
{ prop: "order", label: "排序", width: 80 },
|
||||
{ prop: "route_name", label: "路由名称", minWidth: 100, showOverflowTooltip: true },
|
||||
{ prop: "route_path", label: "路由路径", minWidth: 140, showOverflowTooltip: true },
|
||||
{ prop: "permission", label: "权限标识", minWidth: 160, showOverflowTooltip: true },
|
||||
{ prop: "component_path", label: "组件路径", minWidth: 140, showOverflowTooltip: true },
|
||||
{ prop: "active_path", label: "激活路径", minWidth: 100, showOverflowTooltip: true },
|
||||
{ prop: "redirect", label: "重定向", minWidth: 100, showOverflowTooltip: true },
|
||||
{ prop: "link", label: "外链地址", minWidth: 140, showOverflowTooltip: true },
|
||||
{
|
||||
prop: "keep_alive",
|
||||
label: "是否缓存",
|
||||
width: 96,
|
||||
status: {
|
||||
true: { type: "success", text: "是" },
|
||||
false: { type: "danger", text: "否" },
|
||||
},
|
||||
},
|
||||
{
|
||||
prop: "hidden",
|
||||
label: "是否隐藏",
|
||||
width: 96,
|
||||
status: {
|
||||
true: { type: "success", text: "是" },
|
||||
false: { type: "danger", text: "否" },
|
||||
},
|
||||
},
|
||||
{
|
||||
prop: "always_show",
|
||||
label: "显示根路由",
|
||||
width: 108,
|
||||
status: {
|
||||
true: { type: "success", text: "是" },
|
||||
false: { type: "danger", text: "否" },
|
||||
},
|
||||
},
|
||||
{
|
||||
prop: "affix",
|
||||
label: "固定路由",
|
||||
width: 96,
|
||||
status: {
|
||||
true: { type: "success", text: "是" },
|
||||
false: { type: "danger", text: "否" },
|
||||
},
|
||||
},
|
||||
{ prop: "title", label: "菜单标题", minWidth: 100, showOverflowTooltip: true },
|
||||
{
|
||||
prop: "is_iframe",
|
||||
label: "嵌入iframe",
|
||||
width: 100,
|
||||
status: {
|
||||
true: { type: "success", text: "是" },
|
||||
false: { type: "danger", text: "否" },
|
||||
},
|
||||
},
|
||||
{
|
||||
prop: "is_hide_tab",
|
||||
label: "隐藏标签页",
|
||||
width: 100,
|
||||
status: {
|
||||
true: { type: "success", text: "是" },
|
||||
false: { type: "danger", text: "否" },
|
||||
},
|
||||
},
|
||||
{
|
||||
prop: "show_badge",
|
||||
label: "红点角标",
|
||||
width: 96,
|
||||
status: {
|
||||
true: { type: "success", text: "是" },
|
||||
false: { type: "danger", text: "否" },
|
||||
},
|
||||
},
|
||||
{ prop: "show_text_badge", label: "文字角标", width: 100, showOverflowTooltip: true },
|
||||
{
|
||||
prop: "params",
|
||||
label: "路由参数",
|
||||
minWidth: 100,
|
||||
formatter: (row: MenuTable) =>
|
||||
row.params == null
|
||||
? "—"
|
||||
: typeof row.params === "object"
|
||||
? JSON.stringify(row.params)
|
||||
: String(row.params),
|
||||
},
|
||||
{ prop: "description", label: "描述", minWidth: 140, showOverflowTooltip: true },
|
||||
{ prop: "created_time", label: "创建时间", width: 168, showOverflowTooltip: true },
|
||||
{ prop: "updated_time", label: "更新时间", width: 168, showOverflowTooltip: true },
|
||||
{
|
||||
prop: "operation",
|
||||
label: "操作",
|
||||
width: 220,
|
||||
fixed: "right",
|
||||
align: "right",
|
||||
formatter: (row: MenuTable) => formatMenuOperationCell(row, opCtx),
|
||||
},
|
||||
])
|
||||
);
|
||||
|
||||
const rules = reactive({
|
||||
name: [
|
||||
|
||||
@@ -120,7 +120,7 @@ const methodLabel = computed(() => (selectedMethod.value === "alipay" ? "支付
|
||||
async function loadOrderStatus() {
|
||||
try {
|
||||
const res = await OrderAPI.queryPaymentStatus(orderId.value);
|
||||
const data = (res.data as any)?.data;
|
||||
const data = res.data?.data;
|
||||
if (data?.paid) {
|
||||
paid.value = true;
|
||||
orderNo.value = data.order_no || "";
|
||||
@@ -136,7 +136,7 @@ async function startPay() {
|
||||
paying.value = true;
|
||||
try {
|
||||
const res = await OrderAPI.payOrder(orderId.value, { pay_method: selectedMethod.value });
|
||||
const data = (res.data as any)?.data;
|
||||
const data = res.data?.data;
|
||||
orderNo.value = data.order_no;
|
||||
amount.value = data.amount;
|
||||
qrCodeUrl.value = data.qr_code_url || "";
|
||||
@@ -171,7 +171,7 @@ function startPolling() {
|
||||
pollTimer = setInterval(async () => {
|
||||
try {
|
||||
const res = await OrderAPI.queryPaymentStatus(orderId.value);
|
||||
const data = (res.data as any)?.data;
|
||||
const data = res.data?.data;
|
||||
if (data?.paid) {
|
||||
paid.value = true;
|
||||
orderNo.value = data.order_no || orderNo.value;
|
||||
|
||||
@@ -134,16 +134,15 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, h } from "vue";
|
||||
import { ElMessageBox, ElButton, ElTabs, ElTabPane, ElTag } from "element-plus";
|
||||
import { ref, reactive, computed } from "vue";
|
||||
import { ElMessageBox, ElButton, ElTabs, ElTabPane } from "element-plus";
|
||||
import { useTable } from "@/hooks/core/useTable";
|
||||
import { useAuth } from "@/hooks/core/useAuth";
|
||||
import OrderAPI from "@/api/module_platform/order";
|
||||
import type { OrderTable, PaymentRecordTable, RefundTable } from "@/api/module_platform/order";
|
||||
import type { ColumnOption } from "@/types/component";
|
||||
import type { SearchFormItem } from "@/components/forms/fa-search-bar/index.vue";
|
||||
import type { FormItem } from "@/components/forms/fa-form/index.vue";
|
||||
import { renderTableOperationCell, type TableOperationAction } from "@utils";
|
||||
import { renderTableOperationCell, type TableOperationAction, resolveStatusColumns } from "@utils";
|
||||
|
||||
defineOptions({ name: "Order" });
|
||||
|
||||
@@ -165,27 +164,11 @@ function orderTypeLabel(t: string): string {
|
||||
return m[t] || t;
|
||||
}
|
||||
|
||||
function orderStatusLabel(s: number): string {
|
||||
const m: Record<number, string> = {
|
||||
0: "待支付",
|
||||
1: "已支付",
|
||||
2: "已取消",
|
||||
3: "已退款",
|
||||
4: "已过期",
|
||||
};
|
||||
return m[s] ?? String(s);
|
||||
}
|
||||
|
||||
function payMethodLabel(m: string): string {
|
||||
const mp: Record<string, string> = { alipay: "支付宝", wxpay: "微信支付" };
|
||||
return mp[m] || m;
|
||||
}
|
||||
|
||||
function refundStatusLabel(s: number): string {
|
||||
const m: Record<number, string> = { 0: "待审核", 1: "已批准", 2: "已驳回", 3: "已完成" };
|
||||
return m[s] ?? String(s);
|
||||
}
|
||||
|
||||
// ══════════════════ 订单列表 ════════════════════
|
||||
|
||||
function buildOrderRowActions(row: OrderTable): TableOperationAction[] {
|
||||
@@ -254,8 +237,6 @@ const orderSearchItems = computed<SearchFormItem[]>(() => [
|
||||
},
|
||||
]);
|
||||
|
||||
const orderSearchBarRef = ref();
|
||||
|
||||
const {
|
||||
columns: orderColumns,
|
||||
columnChecks: orderColumnChecks,
|
||||
@@ -274,7 +255,7 @@ const {
|
||||
page_no: 1,
|
||||
page_size: 20,
|
||||
},
|
||||
columnsFactory: (): ColumnOption<OrderTable>[] => [
|
||||
columnsFactory: resolveStatusColumns<OrderTable>(() => [
|
||||
{ prop: "id", label: "ID", width: 60 },
|
||||
{ prop: "order_no", label: "订单号", minWidth: 180, showOverflowTooltip: true },
|
||||
{ prop: "tenant_id", label: "租户ID", width: 80 },
|
||||
@@ -283,8 +264,12 @@ const {
|
||||
prop: "order_type",
|
||||
label: "类型",
|
||||
width: 90,
|
||||
formatter: (row: OrderTable) =>
|
||||
h(ElTag, { size: "small" as const }, () => orderTypeLabel(row.order_type)),
|
||||
status: {
|
||||
new: { type: "info", text: "新购" },
|
||||
renew: { type: "info", text: "续费" },
|
||||
upgrade: { type: "info", text: "升级" },
|
||||
downgrade: { type: "info", text: "降级" },
|
||||
},
|
||||
},
|
||||
{
|
||||
prop: "amount",
|
||||
@@ -296,14 +281,12 @@ const {
|
||||
prop: "status",
|
||||
label: "状态",
|
||||
width: 90,
|
||||
formatter: (row: OrderTable) => {
|
||||
const typeMap = ["warning", "success", "info", "warning", "danger"] as const;
|
||||
const t = typeMap[row.status] || "info";
|
||||
return h(
|
||||
ElTag,
|
||||
{ type: t as "warning" | "success" | "info" | "danger", size: "small" as const },
|
||||
() => orderStatusLabel(row.status)
|
||||
);
|
||||
status: {
|
||||
0: { type: "warning", text: "待支付" },
|
||||
1: { type: "success", text: "已支付" },
|
||||
2: { type: "info", text: "已取消" },
|
||||
3: { type: "danger", text: "已退款" },
|
||||
4: { type: "danger", text: "已过期" },
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -327,7 +310,7 @@ const {
|
||||
align: "center",
|
||||
formatter: (row: OrderTable) => formatOrderOpCell(row),
|
||||
},
|
||||
],
|
||||
]),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -422,7 +405,7 @@ const {
|
||||
page_no: 1,
|
||||
page_size: 20,
|
||||
},
|
||||
columnsFactory: (): ColumnOption<PaymentRecordTable>[] => [
|
||||
columnsFactory: resolveStatusColumns<PaymentRecordTable>(() => [
|
||||
{ prop: "id", label: "ID", width: 60 },
|
||||
{ prop: "order_id", label: "订单ID", width: 80 },
|
||||
{
|
||||
@@ -448,19 +431,14 @@ const {
|
||||
prop: "status",
|
||||
label: "状态",
|
||||
width: 90,
|
||||
formatter: (row: PaymentRecordTable) =>
|
||||
h(
|
||||
ElTag,
|
||||
{
|
||||
type: (row.status === 1 ? "success" : "danger") as "success" | "danger",
|
||||
size: "small" as const,
|
||||
},
|
||||
() => (row.status === 1 ? "成功" : "失败")
|
||||
),
|
||||
status: {
|
||||
1: { type: "success", text: "成功" },
|
||||
0: { type: "danger", text: "失败" },
|
||||
},
|
||||
},
|
||||
{ prop: "pay_time", label: "支付时间", width: 160, showOverflowTooltip: true },
|
||||
{ prop: "created_time", label: "创建时间", width: 160, showOverflowTooltip: true },
|
||||
],
|
||||
]),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -516,8 +494,6 @@ const refundSearchItems = computed<SearchFormItem[]>(() => [
|
||||
},
|
||||
]);
|
||||
|
||||
const refundSearchBarRef = ref();
|
||||
|
||||
const {
|
||||
columns: refundColumns,
|
||||
columnChecks: refundColumnChecks,
|
||||
@@ -536,7 +512,7 @@ const {
|
||||
page_no: 1,
|
||||
page_size: 20,
|
||||
},
|
||||
columnsFactory: (): ColumnOption<RefundTable>[] => [
|
||||
columnsFactory: resolveStatusColumns<RefundTable>(() => [
|
||||
{ prop: "id", label: "ID", width: 60 },
|
||||
{ prop: "refund_no", label: "退款单号", minWidth: 180, showOverflowTooltip: true },
|
||||
{ prop: "order_id", label: "订单ID", width: 80 },
|
||||
@@ -551,14 +527,11 @@ const {
|
||||
prop: "status",
|
||||
label: "状态",
|
||||
width: 100,
|
||||
formatter: (row: RefundTable) => {
|
||||
const typeMap = ["warning", "success", "danger", "info"] as const;
|
||||
const t = typeMap[row.status] || "info";
|
||||
return h(
|
||||
ElTag,
|
||||
{ type: t as "warning" | "success" | "info" | "danger", size: "small" as const },
|
||||
() => refundStatusLabel(row.status)
|
||||
);
|
||||
status: {
|
||||
0: { type: "warning", text: "待审核" },
|
||||
1: { type: "success", text: "已批准" },
|
||||
2: { type: "danger", text: "已驳回" },
|
||||
3: { type: "info", text: "已完成" },
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -578,7 +551,7 @@ const {
|
||||
align: "center",
|
||||
formatter: (row: RefundTable) => formatRefundOpCell(row),
|
||||
},
|
||||
],
|
||||
]),
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -141,18 +141,15 @@ import { useCrudDialog } from "@/hooks/core/useCrudDialog";
|
||||
import { useTableSelection } from "@/hooks/core/useTableSelection";
|
||||
import { useCrudForm } from "@/hooks/core/useCrudForm";
|
||||
import { confirmDelete, confirmBatchDelete, confirmToggleStatus } from "@/hooks/core/useConfirm";
|
||||
import type { ColumnOption } from "@/types/component";
|
||||
import PackageAPI, { type PackageForm, type PackageTable } from "@/api/module_platform/package";
|
||||
import MenuAPI from "@/api/module_platform/menu";
|
||||
import { useAuth } from "@/hooks/core/useAuth";
|
||||
import type { SearchFormItem } from "@/components/forms/fa-search-bar/index.vue";
|
||||
import type FaSearchBar from "@/components/forms/fa-search-bar/index.vue";
|
||||
import type { FormItem } from "@/components/forms/fa-form/index.vue";
|
||||
import FaSearchBar from "@/components/forms/fa-search-bar/index.vue";
|
||||
import FaForm from "@/components/forms/fa-form/index.vue";
|
||||
import { renderTableOperationCell, type TableOperationAction } from "@utils";
|
||||
import { ElTag, ElMessage } from "element-plus";
|
||||
import FaDrawer from "@/components/modal/fa-drawer/index.vue";
|
||||
import FaMenuTreeTable from "@/components/others/fa-menu-tree-drawer/index.vue";
|
||||
import FaMenuTreeTable from "@/components/others/fa-menu-tree-table/index.vue";
|
||||
import { renderTableOperationCell, type TableOperationAction, resolveStatusColumns } from "@utils";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
defineOptions({
|
||||
name: "Package",
|
||||
@@ -311,7 +308,7 @@ const {
|
||||
page_no: 1,
|
||||
page_size: 10,
|
||||
},
|
||||
columnsFactory: (): ColumnOption<PackageTable>[] => [
|
||||
columnsFactory: resolveStatusColumns<PackageTable>(() => [
|
||||
{ type: "selection", width: 48, fixed: "left" },
|
||||
{ type: "globalIndex", width: 56, label: "序号" },
|
||||
{ prop: "name", label: "套餐名称", minWidth: 120, showOverflowTooltip: true },
|
||||
@@ -320,10 +317,10 @@ const {
|
||||
prop: "status",
|
||||
label: "状态",
|
||||
width: 80,
|
||||
formatter: (row: PackageTable) =>
|
||||
h(ElTag, { type: row.status === 0 ? "success" : "danger" }, () =>
|
||||
row.status === 0 ? "正常" : "禁用"
|
||||
),
|
||||
status: {
|
||||
0: { type: "success", text: "正常" },
|
||||
1: { type: "danger", text: "禁用" },
|
||||
},
|
||||
},
|
||||
{ prop: "sort", label: "排序", width: 60, align: "center" },
|
||||
{
|
||||
@@ -353,7 +350,7 @@ const {
|
||||
align: "right",
|
||||
formatter: (row: PackageTable) => formatPkgOperationCell(row, opCtx),
|
||||
},
|
||||
],
|
||||
]),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -580,9 +577,14 @@ const { submitLoading, handleCloseDialog, handleOpenDialog, handleSubmit } =
|
||||
dialogVisible,
|
||||
dataFormRef,
|
||||
formRenderKey: pkgFormRenderKey,
|
||||
detailApi: PackageAPI.detailPackage as any,
|
||||
createApi: PackageAPI.createPackage as any,
|
||||
updateApi: PackageAPI.updatePackage as any,
|
||||
detailApi: PackageAPI.detailPackage as unknown as (
|
||||
id: number
|
||||
) => Promise<{ data: { data?: PackageForm } }>,
|
||||
createApi: PackageAPI.createPackage as unknown as (form: PackageForm) => Promise<unknown>,
|
||||
updateApi: PackageAPI.updatePackage as unknown as (
|
||||
id: number,
|
||||
form: PackageForm
|
||||
) => Promise<unknown>,
|
||||
detailFormData,
|
||||
titles: { create: "新增套餐", update: "修改套餐", detail: "套餐详情" },
|
||||
onCreateSuccess: refreshCreate,
|
||||
|
||||
@@ -254,9 +254,6 @@ import PluginAPI, { type PluginForm, type PluginTable } from "@/api/module_platf
|
||||
import { useAuth } from "@/hooks/core/useAuth";
|
||||
import type { SearchFormItem } from "@/components/forms/fa-search-bar/index.vue";
|
||||
import type { FormItem } from "@/components/forms/fa-form/index.vue";
|
||||
import FaSearchBar from "@/components/forms/fa-search-bar/index.vue";
|
||||
import FaForm from "@/components/forms/fa-form/index.vue";
|
||||
import FaPagination from "@/components/others/fa-pagination/index.vue";
|
||||
import {
|
||||
ElTag,
|
||||
ElMessage,
|
||||
@@ -276,10 +273,10 @@ defineOptions({
|
||||
const { hasAuth } = useAuth();
|
||||
|
||||
// ─── 搜索表单 ───
|
||||
const searchForm = ref<{ name?: string; category?: string; status?: string }>({
|
||||
const searchForm = ref<{ name?: string; category?: string; status?: number }>({
|
||||
name: "",
|
||||
category: "",
|
||||
status: "",
|
||||
status: undefined,
|
||||
});
|
||||
const showSearchBar = ref(true);
|
||||
|
||||
@@ -331,8 +328,7 @@ async function fetchData() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await PluginAPI.list(
|
||||
{ page_no: pageNo.value, page_size: pageSize.value },
|
||||
searchForm.value as any
|
||||
{ page_no: pageNo.value, page_size: pageSize.value, ...searchForm.value }
|
||||
);
|
||||
const result = res.data?.data;
|
||||
data.value = (result?.items as PluginTable[]) || [];
|
||||
@@ -356,14 +352,14 @@ async function handleSearchBarSearch(params: Record<string, unknown>) {
|
||||
searchForm.value = {
|
||||
name: (params.name as string) ?? "",
|
||||
category: (params.category as string) ?? "",
|
||||
status: (params.status as string) ?? "",
|
||||
status: params.status !== undefined ? Number(params.status) : undefined,
|
||||
};
|
||||
pageNo.value = 1;
|
||||
await fetchData();
|
||||
}
|
||||
|
||||
async function onResetSearch() {
|
||||
searchForm.value = { name: "", category: "", status: "" };
|
||||
searchForm.value = { name: "", category: "", status: undefined };
|
||||
pageNo.value = 1;
|
||||
await fetchData();
|
||||
}
|
||||
@@ -580,7 +576,9 @@ const { submitLoading, handleCloseDialog, handleOpenDialog, handleSubmit } =
|
||||
dialogVisible,
|
||||
dataFormRef,
|
||||
formRenderKey: pluginFormRenderKey,
|
||||
detailApi: PluginAPI.detail as any,
|
||||
detailApi: PluginAPI.detail as unknown as (
|
||||
id: number
|
||||
) => Promise<{ data: { data?: PluginForm } }>,
|
||||
createApi: PluginAPI.create,
|
||||
updateApi: PluginAPI.update,
|
||||
titles: { create: "新增插件", update: "编辑插件", detail: "插件详情" },
|
||||
|
||||
@@ -274,9 +274,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, h, onMounted } from "vue";
|
||||
import { ref, computed, onMounted } from "vue";
|
||||
import type { TabPaneName } from "element-plus";
|
||||
import { ElTag, ElButton } from "element-plus";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useTable } from "@/hooks/core/useTable";
|
||||
import FaStatsCard from "@/components/cards/fa-stats-card/index.vue";
|
||||
@@ -290,7 +289,7 @@ import type {
|
||||
SelfServiceOrderItem,
|
||||
WorkspaceData,
|
||||
} from "@/api/module_platform/self_service";
|
||||
import type { ColumnOption } from "@/types/component";
|
||||
import { resolveStatusColumns } from "@utils";
|
||||
import type { DescriptionsItem } from "@/components/others/fa-descriptions/index.vue";
|
||||
|
||||
defineOptions({ name: "SelfService" });
|
||||
@@ -354,15 +353,19 @@ const {
|
||||
page_no: 1,
|
||||
page_size: 20,
|
||||
},
|
||||
columnsFactory: (): ColumnOption<SelfServiceOrderItem>[] => [
|
||||
columnsFactory: resolveStatusColumns<SelfServiceOrderItem>(() => [
|
||||
{ prop: "order_no", label: "订单号", minWidth: 200, showOverflowTooltip: true },
|
||||
{ prop: "package_name", label: "套餐", width: 120 },
|
||||
{
|
||||
prop: "order_type",
|
||||
label: "类型",
|
||||
width: 100,
|
||||
formatter: (row: SelfServiceOrderItem) =>
|
||||
h(ElTag, { size: "small" as const }, () => orderTypeLabel(row.order_type)),
|
||||
status: {
|
||||
new: { type: "info", text: "新购" },
|
||||
renew: { type: "info", text: "续费" },
|
||||
upgrade: { type: "info", text: "升级" },
|
||||
downgrade: { type: "info", text: "降级" },
|
||||
},
|
||||
},
|
||||
{
|
||||
prop: "amount",
|
||||
@@ -374,14 +377,11 @@ const {
|
||||
prop: "status",
|
||||
label: "状态",
|
||||
width: 100,
|
||||
formatter: (row: SelfServiceOrderItem) => {
|
||||
const typeMap = ["warning", "success", "info", "danger"] as const;
|
||||
const t = typeMap[row.status] || "info";
|
||||
return h(
|
||||
ElTag,
|
||||
{ type: t as "warning" | "success" | "info" | "danger", size: "small" as const },
|
||||
() => orderStatusLabel(row.status)
|
||||
);
|
||||
status: {
|
||||
0: { type: "warning", text: "待支付" },
|
||||
1: { type: "success", text: "已支付" },
|
||||
2: { type: "info", text: "已取消" },
|
||||
3: { type: "danger", text: "已退款" },
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -392,7 +392,7 @@ const {
|
||||
},
|
||||
{ prop: "pay_time", label: "支付时间", width: 160, showOverflowTooltip: true },
|
||||
{ prop: "created_at", label: "创建时间", width: 160, showOverflowTooltip: true },
|
||||
],
|
||||
]),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -435,21 +435,6 @@ function actionTypeTag(
|
||||
return map[action];
|
||||
}
|
||||
|
||||
function orderTypeLabel(type: string): string {
|
||||
const map: Record<string, string> = {
|
||||
buy: "新购",
|
||||
renew: "续费",
|
||||
upgrade: "升级",
|
||||
downgrade: "降级",
|
||||
};
|
||||
return map[type] || type;
|
||||
}
|
||||
|
||||
function orderStatusLabel(status: number): string {
|
||||
const map: Record<number, string> = { 0: "待支付", 1: "已支付", 2: "已取消", 3: "已退款" };
|
||||
return map[status] || String(status);
|
||||
}
|
||||
|
||||
// ─── 加载 ───
|
||||
async function loadPackages() {
|
||||
packagesLoading.value = true;
|
||||
@@ -497,10 +482,10 @@ async function confirmAction() {
|
||||
package_id: currentPackage.value.id,
|
||||
order_type: currentAction.value as "buy" | "renew" | "upgrade" | "downgrade",
|
||||
});
|
||||
const orderData = res?.data as { id: number; order_no: string; amount?: number } | undefined;
|
||||
const orderData = res?.data as { order_id: number; amount?: number } | undefined;
|
||||
actionDialogVisible.value = false;
|
||||
if (orderData?.id && (orderData?.amount || 0) > 0) {
|
||||
router.push(`/payment/${orderData.id}`);
|
||||
if (orderData?.order_id && (orderData?.amount || 0) > 0) {
|
||||
router.push(`/payment/${orderData.order_id}`);
|
||||
} else {
|
||||
activeTab.value = "orders";
|
||||
getOrderData();
|
||||
|
||||
@@ -206,7 +206,6 @@ import { useTable } from "@/hooks/core/useTable";
|
||||
import { useCrudDialog } from "@/hooks/core/useCrudDialog";
|
||||
import { useTableSelection } from "@/hooks/core/useTableSelection";
|
||||
import { confirmDelete, confirmBatchDelete } from "@/hooks/core/useConfirm";
|
||||
import type { ColumnOption } from "@/types/component";
|
||||
import TenantAPI, {
|
||||
type TenantCreateForm,
|
||||
type TenantForm,
|
||||
@@ -214,22 +213,12 @@ import TenantAPI, {
|
||||
type TenantUpdateForm,
|
||||
} from "@/api/module_platform/tenant";
|
||||
import { useAuth } from "@/hooks/core/useAuth";
|
||||
import { renderTableOperationCell, type TableOperationAction } from "@utils";
|
||||
import FaSearchBar from "@/components/forms/fa-search-bar/index.vue";
|
||||
import FaForm from "@/components/forms/fa-form/index.vue";
|
||||
import FaUpload from "@/components/others/fa-upload/index.vue";
|
||||
import { renderTableOperationCell, type TableOperationAction, resolveStatusColumns } from "@utils";
|
||||
import type { SearchFormItem } from "@/components/forms/fa-search-bar/index.vue";
|
||||
import type FaSearchBar from "@/components/forms/fa-search-bar/index.vue";
|
||||
import type { FormItem } from "@/components/forms/fa-form/index.vue";
|
||||
import {
|
||||
ElTag,
|
||||
ElMessage,
|
||||
ElTabs,
|
||||
ElTabPane,
|
||||
ElForm,
|
||||
ElFormItem,
|
||||
ElRow,
|
||||
ElCol,
|
||||
} from "element-plus";
|
||||
import type FaForm from "@/components/forms/fa-form/index.vue";
|
||||
import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElRow, ElCol } from "element-plus";
|
||||
import { h, ref } from "vue";
|
||||
|
||||
defineOptions({
|
||||
@@ -242,7 +231,7 @@ const { hasAuth } = useAuth();
|
||||
type TenantSearchForm = {
|
||||
name?: string;
|
||||
code?: string;
|
||||
status?: string;
|
||||
status?: number;
|
||||
created_time?: string[];
|
||||
};
|
||||
|
||||
@@ -420,19 +409,19 @@ const {
|
||||
page_no: 1,
|
||||
page_size: 10,
|
||||
},
|
||||
columnsFactory: (): ColumnOption<TenantTable>[] => [
|
||||
columnsFactory: resolveStatusColumns<TenantTable>(() => [
|
||||
{ type: "selection", width: 48, fixed: "left" },
|
||||
{ type: "globalIndex", width: 56, label: "序号" },
|
||||
{ prop: "name", label: "租户名称", minWidth: 120, showOverflowTooltip: true },
|
||||
{ prop: "code", label: "租户编码", minWidth: 80, showOverflowTooltip: true },
|
||||
{ prop: "code", label: "租户编码", minWidth: 120, showOverflowTooltip: true },
|
||||
{
|
||||
prop: "status",
|
||||
label: "状态",
|
||||
width: 80,
|
||||
formatter: (row: TenantTable) =>
|
||||
h(ElTag, { type: row.status === 0 ? "success" : "danger" }, () =>
|
||||
row.status === 0 ? "正常" : "禁用"
|
||||
),
|
||||
status: {
|
||||
0: { type: "success", text: "正常" },
|
||||
1: { type: "danger", text: "禁用" },
|
||||
},
|
||||
},
|
||||
{ prop: "contact_name", label: "联系人", minWidth: 100, showOverflowTooltip: true },
|
||||
{ prop: "contact_phone", label: "联系电话", width: 110, showOverflowTooltip: true },
|
||||
@@ -448,7 +437,7 @@ const {
|
||||
formatter: (row: TenantTable) =>
|
||||
renderTableOperationCell(buildTenantRowActions(row, opCtx)),
|
||||
},
|
||||
],
|
||||
]),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -834,9 +823,9 @@ function onResetSearch() {
|
||||
async function handleSubmit() {
|
||||
const formRef = dataFormRef.value;
|
||||
if (!formRef) return;
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
const valid = await formRef.validate().catch(() => false);
|
||||
const valid = await (formRef as unknown as { validate: () => Promise<boolean> })
|
||||
.validate()
|
||||
.catch(() => false);
|
||||
if (!valid) return;
|
||||
submitLoading.value = true;
|
||||
const id = formData.value.id as number | undefined;
|
||||
|
||||
Reference in New Issue
Block a user