updates
This commit is contained in:
parent
94d28b4805
commit
3f214226b5
|
|
@ -15,3 +15,52 @@
|
||||||
1. 提取并扁平化嵌套路由中标记为 `constant` 的页面。
|
1. 提取并扁平化嵌套路由中标记为 `constant` 的页面。
|
||||||
2. 动态模式初始化常量路由时合并上述嵌套常量路由并去重。
|
2. 动态模式初始化常量路由时合并上述嵌套常量路由并去重。
|
||||||
- **执行结果**: 动态模式下 `system_oss-config` 已注册到路由表,跳转不再报错。
|
- **执行结果**: 动态模式下 `system_oss-config` 已注册到路由表,跳转不再报错。
|
||||||
|
|
||||||
|
## 会话 ID: 2026-03-22-03
|
||||||
|
- [2026-03-22 15:00:30]
|
||||||
|
- **执行原因**: 客户用户列表增加平台关联跳转,并提供操作列固定的通用能力
|
||||||
|
- **执行过程**:
|
||||||
|
1. 在表格 hook 中新增操作列固定配置,并在用户列表启用固定到右侧。
|
||||||
|
2. 用户列表操作栏新增“平台”按钮并携带用户 ID 跳转到平台用户页。
|
||||||
|
3. 平台用户页读取路由 query.userId 并联动筛选。
|
||||||
|
- **执行结果**: 用户列表可直接跳转平台用户关联页,操作列可固定显示,平台页支持按用户 ID 过滤。
|
||||||
|
|
||||||
|
## 会话 ID: 2026-03-22-04
|
||||||
|
- [2026-03-22 15:04:47]
|
||||||
|
- **执行原因**: 用户列表“平台”改为弹窗关联显示,避免页面跳转
|
||||||
|
- **执行过程**:
|
||||||
|
1. 用户列表引入平台用户列表组件并通过弹窗展示。
|
||||||
|
2. 平台用户列表支持接收预设用户 ID,并自动联动筛选。
|
||||||
|
- **执行结果**: 点击“平台”在弹窗中展示对应平台用户关联信息。
|
||||||
|
|
||||||
|
## 会话 ID: 2026-03-22-05
|
||||||
|
- [2026-03-22 17:10:25]
|
||||||
|
- **执行原因**: 弹窗内列表被遮挡,需优化可视区域与滚动
|
||||||
|
- **执行过程**:
|
||||||
|
1. 平台用户列表增加嵌入式布局样式,放开滚动并限制高度。
|
||||||
|
2. 弹窗内容外层增加最大高度与滚动容器。
|
||||||
|
- **执行结果**: 弹窗内列表显示完整且可滚动查看。
|
||||||
|
|
||||||
|
## 会话 ID: 2026-03-22-06
|
||||||
|
- [2026-03-22 17:14:33]
|
||||||
|
- **执行原因**: 弹窗内仅显示表头与分页,表格内容被折叠
|
||||||
|
- **执行过程**:
|
||||||
|
1. 嵌入式场景关闭表格 flex 高度以避免容器高度为 0。
|
||||||
|
2. 嵌入式场景调整表格容器 class 以保证自适应高度展示。
|
||||||
|
- **执行结果**: 弹窗内表格内容正常显示。
|
||||||
|
|
||||||
|
## 会话 ID: 2026-03-22-07
|
||||||
|
- [2026-03-22 17:20:06]
|
||||||
|
- **执行原因**: 操作列 fixed 未生效,需确保横向滚动触发
|
||||||
|
- **执行过程**:
|
||||||
|
1. 平台用户列表设置最小 `scroll-x`,保证横向滚动容器存在。
|
||||||
|
2. 表格使用新的 `scroll-x` 以触发固定列效果。
|
||||||
|
- **执行结果**: 固定列在横向滚动时生效。
|
||||||
|
|
||||||
|
## 会话 ID: 2026-03-22-08
|
||||||
|
- [2026-03-22 17:25:37]
|
||||||
|
- **执行原因**: 弹窗内固定列仍无效果,需强化表格布局与滚动
|
||||||
|
- **执行过程**:
|
||||||
|
1. 弹窗嵌入场景提高最小 `scroll-x` 以确保横向滚动。
|
||||||
|
2. 弹窗嵌入场景强制表格 `table-layout` 为 `fixed`。
|
||||||
|
- **执行结果**: 弹窗内固定列可随横向滚动保持固定。
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,18 @@ export type UseNaiveTableOptions<ResponseData, ApiData, Pagination extends boole
|
||||||
* @returns true if the column is visible, false otherwise
|
* @returns true if the column is visible, false otherwise
|
||||||
*/
|
*/
|
||||||
getColumnVisible?: (column: NaiveUI.TableColumn<ApiData>) => boolean;
|
getColumnVisible?: (column: NaiveUI.TableColumn<ApiData>) => boolean;
|
||||||
|
/**
|
||||||
|
* fixed the operate column
|
||||||
|
*
|
||||||
|
* @default undefined
|
||||||
|
*/
|
||||||
|
operateColumnFixed?: 'left' | 'right';
|
||||||
|
/**
|
||||||
|
* the key of operate column
|
||||||
|
*
|
||||||
|
* @default 'operate'
|
||||||
|
*/
|
||||||
|
operateColumnKey?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SELECTION_KEY = '__selection__';
|
const SELECTION_KEY = '__selection__';
|
||||||
|
|
@ -33,8 +45,12 @@ export function useNaiveTable<ResponseData, ApiData>(options: UseNaiveTableOptio
|
||||||
const scope = effectScope();
|
const scope = effectScope();
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
|
|
||||||
|
const columns = () =>
|
||||||
|
applyOperateColumnFixed(options.columns(), options.operateColumnFixed, options.operateColumnKey);
|
||||||
|
|
||||||
const result = useTable<ResponseData, ApiData, NaiveUI.TableColumn<ApiData>, false>({
|
const result = useTable<ResponseData, ApiData, NaiveUI.TableColumn<ApiData>, false>({
|
||||||
...options,
|
...options,
|
||||||
|
columns,
|
||||||
getColumnChecks: cols => getColumnChecks(cols, options.getColumnVisible),
|
getColumnChecks: cols => getColumnChecks(cols, options.getColumnVisible),
|
||||||
getColumns
|
getColumns
|
||||||
});
|
});
|
||||||
|
|
@ -125,9 +141,13 @@ export function useNaivePaginatedTable<ResponseData, ApiData>(
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const columns = () =>
|
||||||
|
applyOperateColumnFixed(options.columns(), options.operateColumnFixed, options.operateColumnKey);
|
||||||
|
|
||||||
const result = useTable<ResponseData, ApiData, NaiveUI.TableColumn<ApiData>, true>({
|
const result = useTable<ResponseData, ApiData, NaiveUI.TableColumn<ApiData>, true>({
|
||||||
...options,
|
...options,
|
||||||
pagination: true,
|
pagination: true,
|
||||||
|
columns,
|
||||||
getColumnChecks: cols => getColumnChecks(cols, options.getColumnVisible),
|
getColumnChecks: cols => getColumnChecks(cols, options.getColumnVisible),
|
||||||
getColumns,
|
getColumns,
|
||||||
onFetched: data => {
|
onFetched: data => {
|
||||||
|
|
@ -180,6 +200,25 @@ export function useNaivePaginatedTable<ResponseData, ApiData>(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function applyOperateColumnFixed<Column extends NaiveUI.TableColumn<any>>(
|
||||||
|
columns: Column[],
|
||||||
|
fixed?: 'left' | 'right',
|
||||||
|
key: string = 'operate'
|
||||||
|
) {
|
||||||
|
if (!fixed) return columns;
|
||||||
|
|
||||||
|
return columns.map(column => {
|
||||||
|
if (isTableColumnHasKey(column) && column.key === key && !column.fixed) {
|
||||||
|
return {
|
||||||
|
...column,
|
||||||
|
fixed
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return column;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function useTableOperate<TableData>(
|
export function useTableOperate<TableData>(
|
||||||
data: Ref<TableData[]>,
|
data: Ref<TableData[]>,
|
||||||
idKey: keyof TableData,
|
idKey: keyof TableData,
|
||||||
|
|
|
||||||
|
|
@ -1,122 +1,170 @@
|
||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
import { ref } from 'vue';
|
import { computed, ref, watch } from "vue";
|
||||||
import { NDivider } from 'naive-ui';
|
import { NDivider } from "naive-ui";
|
||||||
import { fetchBatchDeletePlatformUser, fetchGetPlatformUserList } from '@/service/api/client/platform-user';
|
import { useRoute } from "vue-router";
|
||||||
import { useAppStore } from '@/store/modules/app';
|
import {
|
||||||
import { useAuth } from '@/hooks/business/auth';
|
fetchBatchDeletePlatformUser,
|
||||||
import { useDownload } from '@/hooks/business/download';
|
fetchGetPlatformUserList,
|
||||||
import { defaultTransform, useNaivePaginatedTable, useTableOperate } from '@/hooks/common/table';
|
} from "@/service/api/client/platform-user";
|
||||||
import { $t } from '@/locales';
|
import { useAppStore } from "@/store/modules/app";
|
||||||
import ButtonIcon from '@/components/custom/button-icon.vue';
|
import { useAuth } from "@/hooks/business/auth";
|
||||||
import PlatformUserOperateDrawer from './modules/platform-user-operate-drawer.vue';
|
import { useDownload } from "@/hooks/business/download";
|
||||||
import PlatformUserSearch from './modules/platform-user-search.vue';
|
import {
|
||||||
|
defaultTransform,
|
||||||
|
useNaivePaginatedTable,
|
||||||
|
useTableOperate,
|
||||||
|
} from "@/hooks/common/table";
|
||||||
|
import { $t } from "@/locales";
|
||||||
|
import ButtonIcon from "@/components/custom/button-icon.vue";
|
||||||
|
import PlatformUserOperateDrawer from "./modules/platform-user-operate-drawer.vue";
|
||||||
|
import PlatformUserSearch from "./modules/platform-user-search.vue";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'PlatformUserList'
|
name: "PlatformUserList",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
presetUserId?: CommonType.IdType | null;
|
||||||
|
embedded?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const { download } = useDownload();
|
const { download } = useDownload();
|
||||||
const { hasAuth } = useAuth();
|
const { hasAuth } = useAuth();
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
function getUserIdFromRoute(queryUserId: unknown) {
|
||||||
|
if (Array.isArray(queryUserId)) {
|
||||||
|
return queryUserId[0] ?? null;
|
||||||
|
}
|
||||||
|
if (typeof queryUserId === "string" && queryUserId.trim()) {
|
||||||
|
return queryUserId;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolvedUserId = computed(() => {
|
||||||
|
if (
|
||||||
|
props.presetUserId !== undefined &&
|
||||||
|
props.presetUserId !== null &&
|
||||||
|
String(props.presetUserId).trim()
|
||||||
|
) {
|
||||||
|
return String(props.presetUserId);
|
||||||
|
}
|
||||||
|
return getUserIdFromRoute(route.query.userId);
|
||||||
|
});
|
||||||
|
|
||||||
const searchParams = ref<Api.Client.PlatformUserSearchParams>({
|
const searchParams = ref<Api.Client.PlatformUserSearchParams>({
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
userId: null,
|
userId: resolvedUserId.value,
|
||||||
platformType: null,
|
platformType: null,
|
||||||
platformOpenid: null,
|
platformOpenid: null,
|
||||||
platformUnionid: null,
|
platformUnionid: null,
|
||||||
platformSessionKey: null,
|
platformSessionKey: null,
|
||||||
platformExtra: null,
|
platformExtra: null,
|
||||||
lastLoginTime: null,
|
lastLoginTime: null,
|
||||||
params: {}
|
params: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagination, scrollX } =
|
const {
|
||||||
useNaivePaginatedTable({
|
columns,
|
||||||
|
columnChecks,
|
||||||
|
data,
|
||||||
|
getData,
|
||||||
|
getDataByPage,
|
||||||
|
loading,
|
||||||
|
mobilePagination,
|
||||||
|
scrollX,
|
||||||
|
} = useNaivePaginatedTable({
|
||||||
api: () => fetchGetPlatformUserList(searchParams.value),
|
api: () => fetchGetPlatformUserList(searchParams.value),
|
||||||
transform: response => defaultTransform(response),
|
transform: (response) => defaultTransform(response),
|
||||||
onPaginationParamsChange: params => {
|
onPaginationParamsChange: (params) => {
|
||||||
searchParams.value.pageNum = params.page;
|
searchParams.value.pageNum = params.page;
|
||||||
searchParams.value.pageSize = params.pageSize;
|
searchParams.value.pageSize = params.pageSize;
|
||||||
},
|
},
|
||||||
columns: () => [
|
columns: () => [
|
||||||
{
|
{
|
||||||
type: 'selection',
|
type: "selection",
|
||||||
align: 'center',
|
align: "center",
|
||||||
width: 48
|
width: 48,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'index',
|
key: "index",
|
||||||
title: $t('common.index'),
|
title: $t("common.index"),
|
||||||
align: 'center',
|
align: "center",
|
||||||
width: 64,
|
width: 64,
|
||||||
render: (_, index) => index + 1
|
render: (_, index) => index + 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'id',
|
key: "id",
|
||||||
title: '平台用户ID(自增)',
|
title: "平台用户ID",
|
||||||
align: 'center',
|
align: "center",
|
||||||
minWidth: 120
|
width: 120,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'userId',
|
key: "platformType",
|
||||||
title: '关联t_user.id',
|
title: "平台类型",
|
||||||
align: 'center',
|
align: "center",
|
||||||
minWidth: 120
|
width: 120,
|
||||||
|
render: (row: any) => {
|
||||||
|
if (row.platformType == 1) return <n-tag>微信小程序</n-tag>;
|
||||||
|
if (row.platformType == 2) return <n-tag>抖音小程序</n-tag>;
|
||||||
|
if (row.platformType == 3) return <n-tag>支付宝小程序</n-tag>;
|
||||||
|
return "未知";
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'platformType',
|
key: "platformOpenid",
|
||||||
title: '平台类型:1-微信小程序,2-抖音小程序,3-支付宝小程序',
|
title: "OPENID",
|
||||||
align: 'center',
|
align: "center",
|
||||||
minWidth: 120
|
minWidth: 120,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'platformOpenid',
|
key: "platformUnionid",
|
||||||
title: '平台唯一标识(微信openid/抖音open_id)',
|
title: "平台统一标识(微信unionid,多小程序互通用)",
|
||||||
align: 'center',
|
align: "center",
|
||||||
minWidth: 120
|
minWidth: 120,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'platformUnionid',
|
key: "platformSessionKey",
|
||||||
title: '平台统一标识(微信unionid,多小程序互通用)',
|
title: "平台会话密钥(微信session_key,加密存储)",
|
||||||
align: 'center',
|
align: "center",
|
||||||
minWidth: 120
|
minWidth: 120,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'platformSessionKey',
|
key: "platformExtra",
|
||||||
title: '平台会话密钥(微信session_key,加密存储)',
|
title: "平台扩展字段(如抖音的user_name、微信的city等)",
|
||||||
align: 'center',
|
align: "center",
|
||||||
minWidth: 120
|
minWidth: 120,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'platformExtra',
|
key: "lastLoginTime",
|
||||||
title: '平台扩展字段(如抖音的user_name、微信的city等)',
|
title: "最后登录时间",
|
||||||
align: 'center',
|
align: "center",
|
||||||
minWidth: 120
|
minWidth: 120,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'lastLoginTime',
|
key: "operate",
|
||||||
title: '最后登录时间',
|
title: $t("common.operate"),
|
||||||
align: 'center',
|
align: "center",
|
||||||
minWidth: 120
|
fixed: "right",
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'operate',
|
|
||||||
title: $t('common.operate'),
|
|
||||||
align: 'center',
|
|
||||||
width: 130,
|
width: 130,
|
||||||
render: row => {
|
render: (row) => {
|
||||||
const divider = () => {
|
const divider = () => {
|
||||||
if (!hasAuth('client:platformUser:edit') || !hasAuth('client:platformUser:remove')) {
|
if (
|
||||||
|
!hasAuth("client:platformUser:edit") ||
|
||||||
|
!hasAuth("client:platformUser:remove")
|
||||||
|
) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return <NDivider vertical />;
|
return <NDivider vertical />;
|
||||||
};
|
};
|
||||||
|
|
||||||
const editBtn = () => {
|
const editBtn = () => {
|
||||||
if (!hasAuth('client:platformUser:edit')) {
|
if (!hasAuth("client:platformUser:edit")) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
|
|
@ -124,14 +172,14 @@ const { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagi
|
||||||
text
|
text
|
||||||
type="primary"
|
type="primary"
|
||||||
icon="material-symbols:drive-file-rename-outline-outline"
|
icon="material-symbols:drive-file-rename-outline-outline"
|
||||||
tooltipContent={$t('common.edit')}
|
tooltipContent={$t("common.edit")}
|
||||||
onClick={() => edit(row.id)}
|
onClick={() => edit(row.id)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteBtn = () => {
|
const deleteBtn = () => {
|
||||||
if (!hasAuth('client:platformUser:remove')) {
|
if (!hasAuth("client:platformUser:remove")) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
|
|
@ -139,8 +187,8 @@ const { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagi
|
||||||
text
|
text
|
||||||
type="error"
|
type="error"
|
||||||
icon="material-symbols:delete-outline"
|
icon="material-symbols:delete-outline"
|
||||||
tooltipContent={$t('common.delete')}
|
tooltipContent={$t("common.delete")}
|
||||||
popconfirmContent={$t('common.confirmDelete')}
|
popconfirmContent={$t("common.confirmDelete")}
|
||||||
onPositiveClick={() => handleDelete(row.id)}
|
onPositiveClick={() => handleDelete(row.id)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
@ -153,13 +201,54 @@ const { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagi
|
||||||
{deleteBtn()}
|
{deleteBtn()}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const { drawerVisible, operateType, editingData, handleAdd, handleEdit, checkedRowKeys, onBatchDeleted, onDeleted } =
|
const {
|
||||||
useTableOperate(data, 'id', getData);
|
drawerVisible,
|
||||||
|
operateType,
|
||||||
|
editingData,
|
||||||
|
handleAdd,
|
||||||
|
handleEdit,
|
||||||
|
checkedRowKeys,
|
||||||
|
onBatchDeleted,
|
||||||
|
onDeleted,
|
||||||
|
} = useTableOperate(data, "id", getData);
|
||||||
|
|
||||||
|
const rootClass = computed(() =>
|
||||||
|
props.embedded
|
||||||
|
? "flex-col-stretch gap-12px max-h-80vh overflow-auto"
|
||||||
|
: "min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto",
|
||||||
|
);
|
||||||
|
|
||||||
|
const cardClass = computed(() =>
|
||||||
|
props.embedded ? "card-wrapper" : "card-wrapper sm:flex-1-hidden",
|
||||||
|
);
|
||||||
|
|
||||||
|
const tableFlexHeight = computed(() => !appStore.isMobile && !props.embedded);
|
||||||
|
|
||||||
|
const tableClass = computed(() => (props.embedded ? "w-full" : "sm:h-full"));
|
||||||
|
|
||||||
|
const tableScrollX = computed(() =>
|
||||||
|
props.embedded
|
||||||
|
? Math.max(scrollX.value, 1800)
|
||||||
|
: Math.max(scrollX.value, 1600),
|
||||||
|
);
|
||||||
|
|
||||||
|
const tableLayout = computed(() => (props.embedded ? "fixed" : "auto"));
|
||||||
|
|
||||||
|
function syncUserIdFromRoute() {
|
||||||
|
const nextUserId = resolvedUserId.value;
|
||||||
|
if (searchParams.value.userId === nextUserId) return;
|
||||||
|
searchParams.value.userId = nextUserId;
|
||||||
|
getDataByPage(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(resolvedUserId, () => {
|
||||||
|
syncUserIdFromRoute();
|
||||||
|
});
|
||||||
|
|
||||||
async function handleBatchDelete() {
|
async function handleBatchDelete() {
|
||||||
// request
|
// request
|
||||||
|
|
@ -180,14 +269,23 @@ function edit(id: CommonType.IdType) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleExport() {
|
function handleExport() {
|
||||||
download('/client/platformUser/export', searchParams.value, `平台用户关联(微信/抖音小程序用户信息)_${new Date().getTime()}.xlsx`);
|
download(
|
||||||
|
"/client/platformUser/export",
|
||||||
|
searchParams.value,
|
||||||
|
`平台用户关联(微信/抖音小程序用户信息)_${new Date().getTime()}.xlsx`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
|
<div :class="rootClass">
|
||||||
<PlatformUserSearch v-model:model="searchParams" @search="getDataByPage" />
|
<PlatformUserSearch v-model:model="searchParams" @search="getDataByPage" />
|
||||||
<NCard title="平台用户关联(微信/抖音小程序用户信息)列表" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
|
<NCard
|
||||||
|
title="平台用户关联(微信/抖音小程序用户信息)列表"
|
||||||
|
:bordered="false"
|
||||||
|
size="small"
|
||||||
|
:class="cardClass"
|
||||||
|
>
|
||||||
<template #header-extra>
|
<template #header-extra>
|
||||||
<TableHeaderOperation
|
<TableHeaderOperation
|
||||||
v-model:columns="columnChecks"
|
v-model:columns="columnChecks"
|
||||||
|
|
@ -207,13 +305,14 @@ function handleExport() {
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:data="data"
|
:data="data"
|
||||||
size="small"
|
size="small"
|
||||||
:flex-height="!appStore.isMobile"
|
:flex-height="tableFlexHeight"
|
||||||
:scroll-x="scrollX"
|
:table-layout="tableLayout"
|
||||||
|
:scroll-x="tableScrollX"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
remote
|
remote
|
||||||
:row-key="row => row.id"
|
:row-key="(row) => row.id"
|
||||||
:pagination="mobilePagination"
|
:pagination="mobilePagination"
|
||||||
class="sm:h-full"
|
:class="tableClass"
|
||||||
/>
|
/>
|
||||||
<PlatformUserOperateDrawer
|
<PlatformUserOperateDrawer
|
||||||
v-model:visible="drawerVisible"
|
v-model:visible="drawerVisible"
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, watch } from 'vue';
|
import { computed, ref, watch } from "vue";
|
||||||
import { jsonClone } from '@sa/utils';
|
import { jsonClone } from "@sa/utils";
|
||||||
import { fetchCreatePlatformUser, fetchUpdatePlatformUser } from '@/service/api/client/platform-user';
|
import {
|
||||||
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
|
fetchCreatePlatformUser,
|
||||||
import { $t } from '@/locales';
|
fetchUpdatePlatformUser,
|
||||||
|
} from "@/service/api/client/platform-user";
|
||||||
|
import { useFormRules, useNaiveForm } from "@/hooks/common/form";
|
||||||
|
import { $t } from "@/locales";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'PlatformUserOperateDrawer'
|
name: "PlatformUserOperateDrawer",
|
||||||
});
|
});
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|
@ -19,13 +22,13 @@ interface Props {
|
||||||
const props = defineProps<Props>();
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
interface Emits {
|
interface Emits {
|
||||||
(e: 'submitted'): void;
|
(e: "submitted"): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const emit = defineEmits<Emits>();
|
const emit = defineEmits<Emits>();
|
||||||
|
|
||||||
const visible = defineModel<boolean>('visible', {
|
const visible = defineModel<boolean>("visible", {
|
||||||
default: false
|
default: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
const { formRef, validate, restoreValidation } = useNaiveForm();
|
||||||
|
|
@ -33,8 +36,8 @@ const { createRequiredRule } = useFormRules();
|
||||||
|
|
||||||
const title = computed(() => {
|
const title = computed(() => {
|
||||||
const titles: Record<NaiveUI.TableOperateType, string> = {
|
const titles: Record<NaiveUI.TableOperateType, string> = {
|
||||||
add: '新增平台用户关联(微信/抖音小程序用户信息)',
|
add: "新增平台用户关联(微信/抖音小程序用户信息)",
|
||||||
edit: '编辑平台用户关联(微信/抖音小程序用户信息)'
|
edit: "编辑平台用户关联(微信/抖音小程序用户信息)",
|
||||||
};
|
};
|
||||||
return titles[props.operateType];
|
return titles[props.operateType];
|
||||||
});
|
});
|
||||||
|
|
@ -48,33 +51,34 @@ function createDefaultModel(): Model {
|
||||||
id: null,
|
id: null,
|
||||||
userId: null,
|
userId: null,
|
||||||
platformType: null,
|
platformType: null,
|
||||||
platformOpenid: '',
|
platformOpenid: "",
|
||||||
platformUnionid: '',
|
platformUnionid: "",
|
||||||
platformSessionKey: '',
|
platformSessionKey: "",
|
||||||
platformExtra: '',
|
platformExtra: "",
|
||||||
lastLoginTime: null,
|
lastLoginTime: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
type RuleKey = Extract<
|
type RuleKey = Extract<
|
||||||
keyof Model,
|
keyof Model,
|
||||||
| 'id'
|
"id" | "userId" | "platformType" | "platformOpenid"
|
||||||
| 'userId'
|
|
||||||
| 'platformType'
|
|
||||||
| 'platformOpenid'
|
|
||||||
>;
|
>;
|
||||||
|
|
||||||
const rules: Record<RuleKey, App.Global.FormRule> = {
|
const rules: Record<RuleKey, App.Global.FormRule> = {
|
||||||
id: createRequiredRule('平台用户ID(自增)不能为空'),
|
id: createRequiredRule("平台用户ID(自增)不能为空"),
|
||||||
userId: createRequiredRule('关联t_user.id不能为空'),
|
userId: createRequiredRule("关联t_user.id不能为空"),
|
||||||
platformType: createRequiredRule('平台类型:1-微信小程序,2-抖音小程序,3-支付宝小程序不能为空'),
|
platformType: createRequiredRule(
|
||||||
platformOpenid: createRequiredRule('平台唯一标识(微信openid/抖音open_id)不能为空'),
|
"平台类型:1-微信小程序,2-抖音小程序,3-支付宝小程序不能为空",
|
||||||
|
),
|
||||||
|
platformOpenid: createRequiredRule(
|
||||||
|
"平台唯一标识(微信openid/抖音open_id)不能为空",
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
function handleUpdateModelWhenEdit() {
|
function handleUpdateModelWhenEdit() {
|
||||||
model.value = createDefaultModel();
|
model.value = createDefaultModel();
|
||||||
|
|
||||||
if (props.operateType === 'edit' && props.rowData) {
|
if (props.operateType === "edit" && props.rowData) {
|
||||||
Object.assign(model.value, jsonClone(props.rowData));
|
Object.assign(model.value, jsonClone(props.rowData));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -86,41 +90,78 @@ function closeDrawer() {
|
||||||
async function handleSubmit() {
|
async function handleSubmit() {
|
||||||
await validate();
|
await validate();
|
||||||
|
|
||||||
const { id, userId, platformType, platformOpenid, platformUnionid, platformSessionKey, platformExtra, lastLoginTime } = model.value;
|
const {
|
||||||
|
id,
|
||||||
|
userId,
|
||||||
|
platformType,
|
||||||
|
platformOpenid,
|
||||||
|
platformUnionid,
|
||||||
|
platformSessionKey,
|
||||||
|
platformExtra,
|
||||||
|
lastLoginTime,
|
||||||
|
} = model.value;
|
||||||
|
|
||||||
// request
|
// request
|
||||||
if (props.operateType === 'add') {
|
if (props.operateType === "add") {
|
||||||
const { error } = await fetchCreatePlatformUser({ userId, platformType, platformOpenid, platformUnionid, platformSessionKey, platformExtra, lastLoginTime });
|
const { error } = await fetchCreatePlatformUser({
|
||||||
|
userId,
|
||||||
|
platformType,
|
||||||
|
platformOpenid,
|
||||||
|
platformUnionid,
|
||||||
|
platformSessionKey,
|
||||||
|
platformExtra,
|
||||||
|
lastLoginTime,
|
||||||
|
});
|
||||||
if (error) return;
|
if (error) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.operateType === 'edit') {
|
if (props.operateType === "edit") {
|
||||||
const { error } = await fetchUpdatePlatformUser({ id, userId, platformType, platformOpenid, platformUnionid, platformSessionKey, platformExtra, lastLoginTime });
|
const { error } = await fetchUpdatePlatformUser({
|
||||||
|
id,
|
||||||
|
userId,
|
||||||
|
platformType,
|
||||||
|
platformOpenid,
|
||||||
|
platformUnionid,
|
||||||
|
platformSessionKey,
|
||||||
|
platformExtra,
|
||||||
|
lastLoginTime,
|
||||||
|
});
|
||||||
if (error) return;
|
if (error) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.$message?.success($t('common.updateSuccess'));
|
window.$message?.success($t("common.updateSuccess"));
|
||||||
closeDrawer();
|
closeDrawer();
|
||||||
emit('submitted');
|
emit("submitted");
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(visible, () => {
|
watch(visible, () => {
|
||||||
if (visible.value) {
|
if (visible.value) {
|
||||||
handleUpdateModelWhenEdit();
|
handleUpdateModelWhenEdit();
|
||||||
restoreValidation();
|
restoreValidation();
|
||||||
getTreeList();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NDrawer v-model:show="visible" :title="title" display-directive="show" :width="800" class="max-w-90%">
|
<NDrawer
|
||||||
|
v-model:show="visible"
|
||||||
|
:title="title"
|
||||||
|
display-directive="show"
|
||||||
|
:width="800"
|
||||||
|
class="max-w-90%"
|
||||||
|
>
|
||||||
<NDrawerContent :title="title" :native-scrollbar="false" closable>
|
<NDrawerContent :title="title" :native-scrollbar="false" closable>
|
||||||
<NForm ref="formRef" :model="model" :rules="rules">
|
<NForm ref="formRef" :model="model" :rules="rules">
|
||||||
<NFormItem label="关联t_user.id" path="userId">
|
<NFormItem label="关联t_user.id" path="userId">
|
||||||
<NInput v-model:value="model.userId" placeholder="请输入关联t_user.id" />
|
<NInput
|
||||||
|
v-model:value="model.userId"
|
||||||
|
placeholder="请输入关联t_user.id"
|
||||||
|
/>
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
<NFormItem label="平台类型:1-微信小程序,2-抖音小程序,3-支付宝小程序" path="platformType">
|
<NFormItem
|
||||||
|
label="平台类型:1-微信小程序,2-抖音小程序,3-支付宝小程序"
|
||||||
|
path="platformType"
|
||||||
|
>
|
||||||
<NSelect
|
<NSelect
|
||||||
v-model:value="model.platformType"
|
v-model:value="model.platformType"
|
||||||
placeholder="请选择平台类型:1-微信小程序,2-抖音小程序,3-支付宝小程序"
|
placeholder="请选择平台类型:1-微信小程序,2-抖音小程序,3-支付宝小程序"
|
||||||
|
|
@ -128,17 +169,41 @@ watch(visible, () => {
|
||||||
clearable
|
clearable
|
||||||
/>
|
/>
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
<NFormItem label="平台唯一标识(微信openid/抖音open_id)" path="platformOpenid">
|
<NFormItem
|
||||||
<NInput v-model:value="model.platformOpenid" placeholder="请输入平台唯一标识(微信openid/抖音open_id)" />
|
label="平台唯一标识(微信openid/抖音open_id)"
|
||||||
|
path="platformOpenid"
|
||||||
|
>
|
||||||
|
<NInput
|
||||||
|
v-model:value="model.platformOpenid"
|
||||||
|
placeholder="请输入平台唯一标识(微信openid/抖音open_id)"
|
||||||
|
/>
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
<NFormItem label="平台统一标识(微信unionid,多小程序互通用)" path="platformUnionid">
|
<NFormItem
|
||||||
<NInput v-model:value="model.platformUnionid" placeholder="请输入平台统一标识(微信unionid,多小程序互通用)" />
|
label="平台统一标识(微信unionid,多小程序互通用)"
|
||||||
|
path="platformUnionid"
|
||||||
|
>
|
||||||
|
<NInput
|
||||||
|
v-model:value="model.platformUnionid"
|
||||||
|
placeholder="请输入平台统一标识(微信unionid,多小程序互通用)"
|
||||||
|
/>
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
<NFormItem label="平台会话密钥(微信session_key,加密存储)" path="platformSessionKey">
|
<NFormItem
|
||||||
<NInput v-model:value="model.platformSessionKey" placeholder="请输入平台会话密钥(微信session_key,加密存储)" />
|
label="平台会话密钥(微信session_key,加密存储)"
|
||||||
|
path="platformSessionKey"
|
||||||
|
>
|
||||||
|
<NInput
|
||||||
|
v-model:value="model.platformSessionKey"
|
||||||
|
placeholder="请输入平台会话密钥(微信session_key,加密存储)"
|
||||||
|
/>
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
<NFormItem label="平台扩展字段(如抖音的user_name、微信的city等)" path="platformExtra">
|
<NFormItem
|
||||||
<NInput v-model:value="model.platformExtra" placeholder="请输入平台扩展字段(如抖音的user_name、微信的city等)" />
|
label="平台扩展字段(如抖音的user_name、微信的city等)"
|
||||||
|
path="platformExtra"
|
||||||
|
>
|
||||||
|
<NInput
|
||||||
|
v-model:value="model.platformExtra"
|
||||||
|
placeholder="请输入平台扩展字段(如抖音的user_name、微信的city等)"
|
||||||
|
/>
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
<NFormItem label="最后登录时间" path="lastLoginTime">
|
<NFormItem label="最后登录时间" path="lastLoginTime">
|
||||||
<NDatePicker
|
<NDatePicker
|
||||||
|
|
@ -151,8 +216,10 @@ watch(visible, () => {
|
||||||
</NForm>
|
</NForm>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<NSpace :size="16">
|
<NSpace :size="16">
|
||||||
<NButton @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
|
<NButton @click="closeDrawer">{{ $t("common.cancel") }}</NButton>
|
||||||
<NButton type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
|
<NButton type="primary" @click="handleSubmit">{{
|
||||||
|
$t("common.confirm")
|
||||||
|
}}</NButton>
|
||||||
</NSpace>
|
</NSpace>
|
||||||
</template>
|
</template>
|
||||||
</NDrawerContent>
|
</NDrawerContent>
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import {
|
||||||
} from "@/hooks/common/table";
|
} from "@/hooks/common/table";
|
||||||
import { $t } from "@/locales";
|
import { $t } from "@/locales";
|
||||||
import ButtonIcon from "@/components/custom/button-icon.vue";
|
import ButtonIcon from "@/components/custom/button-icon.vue";
|
||||||
|
import PlatformUserList from "@/views/client/platform-user/index.vue";
|
||||||
import UserOperateDrawer from "./modules/user-operate-drawer.vue";
|
import UserOperateDrawer from "./modules/user-operate-drawer.vue";
|
||||||
import UserSearch from "./modules/user-search.vue";
|
import UserSearch from "./modules/user-search.vue";
|
||||||
|
|
||||||
|
|
@ -25,6 +26,8 @@ defineOptions({
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const { download } = useDownload();
|
const { download } = useDownload();
|
||||||
const { hasAuth } = useAuth();
|
const { hasAuth } = useAuth();
|
||||||
|
const platformUserVisible = ref(false);
|
||||||
|
const platformUserId = ref<CommonType.IdType | null>(null);
|
||||||
|
|
||||||
const searchParams = ref<Api.Client.UserSearchParams>({
|
const searchParams = ref<Api.Client.UserSearchParams>({
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
|
|
@ -52,6 +55,7 @@ const {
|
||||||
} = useNaivePaginatedTable({
|
} = useNaivePaginatedTable({
|
||||||
api: () => fetchGetUserList(searchParams.value),
|
api: () => fetchGetUserList(searchParams.value),
|
||||||
transform: (response) => defaultTransform(response),
|
transform: (response) => defaultTransform(response),
|
||||||
|
operateColumnFixed: "right",
|
||||||
onPaginationParamsChange: (params) => {
|
onPaginationParamsChange: (params) => {
|
||||||
searchParams.value.pageNum = params.page;
|
searchParams.value.pageNum = params.page;
|
||||||
searchParams.value.pageSize = params.pageSize;
|
searchParams.value.pageSize = params.pageSize;
|
||||||
|
|
@ -66,7 +70,7 @@ const {
|
||||||
key: "index",
|
key: "index",
|
||||||
title: $t("common.index"),
|
title: $t("common.index"),
|
||||||
align: "center",
|
align: "center",
|
||||||
width: 64,
|
width: 50,
|
||||||
render: (_, index) => index + 1,
|
render: (_, index) => index + 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -92,6 +96,15 @@ const {
|
||||||
title: "用户头像URL",
|
title: "用户头像URL",
|
||||||
align: "center",
|
align: "center",
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
|
render: (row: any) => {
|
||||||
|
return (
|
||||||
|
<img
|
||||||
|
src={row.avatarUrl}
|
||||||
|
alt="avatar"
|
||||||
|
style="width: 50%; transform: translateX(50%);"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "phone",
|
key: "phone",
|
||||||
|
|
@ -105,6 +118,7 @@ const {
|
||||||
align: "center",
|
align: "center",
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
render: (row: any) => {
|
render: (row: any) => {
|
||||||
|
console.log(row.gender);
|
||||||
if (row.gender === 1) return <p>男</p>;
|
if (row.gender === 1) return <p>男</p>;
|
||||||
if (row.gender === 2) return <p>女</p>;
|
if (row.gender === 2) return <p>女</p>;
|
||||||
return <p>未知</p>;
|
return <p>未知</p>;
|
||||||
|
|
@ -126,15 +140,12 @@ const {
|
||||||
align: "center",
|
align: "center",
|
||||||
width: 130,
|
width: 130,
|
||||||
render: (row) => {
|
render: (row) => {
|
||||||
const divider = () => {
|
const canEdit = hasAuth("client:user:edit");
|
||||||
if (!hasAuth("client:user:edit") || !hasAuth("client:user:remove")) {
|
const canDelete = hasAuth("client:user:remove");
|
||||||
return null;
|
const canPlatform = true;
|
||||||
}
|
|
||||||
return <NDivider vertical />;
|
|
||||||
};
|
|
||||||
|
|
||||||
const editBtn = () => {
|
const editBtn = () => {
|
||||||
if (!hasAuth("client:user:edit")) {
|
if (!canEdit) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
|
|
@ -148,8 +159,23 @@ const {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const platformBtn = () => {
|
||||||
|
if (!canPlatform) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<ButtonIcon
|
||||||
|
text
|
||||||
|
type="info"
|
||||||
|
icon="material-symbols:link"
|
||||||
|
tooltipContent="平台"
|
||||||
|
onClick={() => handleToPlatformUser(row.id)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const deleteBtn = () => {
|
const deleteBtn = () => {
|
||||||
if (!hasAuth("client:user:remove")) {
|
if (!canDelete) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
|
|
@ -167,7 +193,9 @@ const {
|
||||||
return (
|
return (
|
||||||
<div class="flex-center gap-8px">
|
<div class="flex-center gap-8px">
|
||||||
{editBtn()}
|
{editBtn()}
|
||||||
{divider()}
|
{canEdit && canPlatform ? <NDivider vertical /> : null}
|
||||||
|
{platformBtn()}
|
||||||
|
{canPlatform && canDelete ? <NDivider vertical /> : null}
|
||||||
{deleteBtn()}
|
{deleteBtn()}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -205,6 +233,11 @@ function edit(id: CommonType.IdType) {
|
||||||
handleEdit(id);
|
handleEdit(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleToPlatformUser(id: CommonType.IdType) {
|
||||||
|
platformUserId.value = id;
|
||||||
|
platformUserVisible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
function handleExport() {
|
function handleExport() {
|
||||||
download(
|
download(
|
||||||
"/client/user/export",
|
"/client/user/export",
|
||||||
|
|
@ -219,6 +252,18 @@ function handleExport() {
|
||||||
class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto"
|
class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto"
|
||||||
>
|
>
|
||||||
<UserSearch v-model:model="searchParams" @search="getDataByPage" />
|
<UserSearch v-model:model="searchParams" @search="getDataByPage" />
|
||||||
|
<NModal
|
||||||
|
v-model:show="platformUserVisible"
|
||||||
|
preset="card"
|
||||||
|
title="平台用户关联"
|
||||||
|
class="max-w-90% w-1200px"
|
||||||
|
size="huge"
|
||||||
|
:bordered="false"
|
||||||
|
>
|
||||||
|
<div class="max-h-80vh overflow-auto">
|
||||||
|
<PlatformUserList :preset-user-id="platformUserId" :embedded="true" />
|
||||||
|
</div>
|
||||||
|
</NModal>
|
||||||
<NCard
|
<NCard
|
||||||
title="客户用户基础信息列表"
|
title="客户用户基础信息列表"
|
||||||
:bordered="false"
|
:bordered="false"
|
||||||
|
|
|
||||||
|
|
@ -162,17 +162,17 @@ watch(visible, () => {
|
||||||
placeholder="请输入手机号"
|
placeholder="请输入手机号"
|
||||||
/>
|
/>
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
<NFormItem label="性别:0-未知,1-男,2-女" path="gender">
|
<NFormItem label="性别" path="gender">
|
||||||
<NRadioGroup v-model:value="model.gender">
|
<NRadioGroup v-model:value="model.gender">
|
||||||
<NRadio value="0">未知</NRadio>
|
<NRadio :value="0">未知</NRadio>
|
||||||
<NRadio value="1">男</NRadio>
|
<NRadio :value="1">男</NRadio>
|
||||||
<NRadio value="2">女</NRadio>
|
<NRadio :value="2">女</NRadio>
|
||||||
</NRadioGroup>
|
</NRadioGroup>
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
<NFormItem label="状态:0-禁用,1-正常" path="status">
|
<NFormItem label="状态" path="status">
|
||||||
<NRadioGroup v-model:value="model.status">
|
<NRadioGroup v-model:value="model.status">
|
||||||
<NRadio value="0">禁用</NRadio>
|
<NRadio :value="0">禁用</NRadio>
|
||||||
<NRadio value="1">正常</NRadio>
|
<NRadio :value="1">正常</NRadio>
|
||||||
</NRadioGroup>
|
</NRadioGroup>
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
</NForm>
|
</NForm>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue