1
This commit is contained in:
parent
22bf3427fe
commit
6f71528fcb
@ -6,10 +6,13 @@ import { useAccessStore } from '@vben/stores';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
deleteBaishunGame,
|
deleteBaishunGame,
|
||||||
|
fetchBaishunCatalog,
|
||||||
|
getBaishunProviderConfig,
|
||||||
importBaishunCatalog,
|
importBaishunCatalog,
|
||||||
|
pageBaishunCatalog,
|
||||||
pageBaishunGames,
|
pageBaishunGames,
|
||||||
saveBaishunGame,
|
saveBaishunGame,
|
||||||
syncBaishunGames,
|
saveBaishunProviderConfig,
|
||||||
} from '#/api/legacy/baishun-game';
|
} from '#/api/legacy/baishun-game';
|
||||||
import { getAllowedSysOrigins } from '#/views/system/shared';
|
import { getAllowedSysOrigins } from '#/views/system/shared';
|
||||||
|
|
||||||
@ -26,6 +29,8 @@ import {
|
|||||||
Select,
|
Select,
|
||||||
Space,
|
Space,
|
||||||
Table,
|
Table,
|
||||||
|
TabPane,
|
||||||
|
Tabs,
|
||||||
Tag,
|
Tag,
|
||||||
message,
|
message,
|
||||||
} from 'antdv-next';
|
} from 'antdv-next';
|
||||||
@ -37,7 +42,7 @@ import {
|
|||||||
|
|
||||||
defineOptions({ name: 'OperateBaishunGameManage' });
|
defineOptions({ name: 'OperateBaishunGameManage' });
|
||||||
|
|
||||||
function createForm(sysOrigin = '') {
|
function createGameForm(sysOrigin = '') {
|
||||||
return {
|
return {
|
||||||
amounts: '',
|
amounts: '',
|
||||||
category: 'OTHER',
|
category: 'OTHER',
|
||||||
@ -58,7 +63,7 @@ function createForm(sysOrigin = '') {
|
|||||||
previewUrl: '',
|
previewUrl: '',
|
||||||
regions: '',
|
regions: '',
|
||||||
safeHeight: 0,
|
safeHeight: 0,
|
||||||
showcase: false as any,
|
showcase: true as any,
|
||||||
sort: 0,
|
sort: 0,
|
||||||
sysOrigin,
|
sysOrigin,
|
||||||
vendorGameId: undefined as any,
|
vendorGameId: undefined as any,
|
||||||
@ -67,6 +72,20 @@ function createForm(sysOrigin = '') {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createProviderForm(sysOrigin = '') {
|
||||||
|
return {
|
||||||
|
sysOrigin,
|
||||||
|
platformBaseUrl: '',
|
||||||
|
appId: undefined as any,
|
||||||
|
appName: '',
|
||||||
|
appChannel: '',
|
||||||
|
appKey: '',
|
||||||
|
gsp: 101 as any,
|
||||||
|
launchCodeTtlSeconds: 300 as any,
|
||||||
|
ssTokenTtlSeconds: 86400 as any,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const accessStore = useAccessStore();
|
const accessStore = useAccessStore();
|
||||||
const sysOriginOptions = computed(() => {
|
const sysOriginOptions = computed(() => {
|
||||||
const options = getAllowedSysOrigins(accessStore.accessCodes || []);
|
const options = getAllowedSysOrigins(accessStore.accessCodes || []);
|
||||||
@ -105,24 +124,37 @@ const orientationOptions = [
|
|||||||
{ label: '2', value: 2 as any },
|
{ label: '2', value: 2 as any },
|
||||||
];
|
];
|
||||||
|
|
||||||
const query = reactive<Record<string, any>>({
|
const activeTab = ref('configured');
|
||||||
|
const contextQuery = reactive({ sysOrigin: '' });
|
||||||
|
const gameQuery = reactive<Record<string, any>>({
|
||||||
cursor: 1,
|
cursor: 1,
|
||||||
keyword: '',
|
keyword: '',
|
||||||
limit: 20,
|
limit: 20,
|
||||||
showcase: '',
|
showcase: '',
|
||||||
sysOrigin: '',
|
});
|
||||||
|
const catalogQuery = reactive<Record<string, any>>({
|
||||||
|
cursor: 1,
|
||||||
|
keyword: '',
|
||||||
|
limit: 20,
|
||||||
});
|
});
|
||||||
|
|
||||||
const loading = ref(false);
|
const gameLoading = ref(false);
|
||||||
const saving = ref(false);
|
const gameSaving = ref(false);
|
||||||
const importing = ref(false);
|
const providerLoading = ref(false);
|
||||||
const syncing = ref(false);
|
const providerSaving = ref(false);
|
||||||
const total = ref(0);
|
const catalogLoading = ref(false);
|
||||||
|
const catalogFetching = ref(false);
|
||||||
|
const catalogImporting = ref(false);
|
||||||
|
const totalGames = ref(0);
|
||||||
|
const totalCatalog = ref(0);
|
||||||
const modalOpen = ref(false);
|
const modalOpen = ref(false);
|
||||||
const list = ref<Array<Record<string, any>>>([]);
|
const gameList = ref<Array<Record<string, any>>>([]);
|
||||||
const form = reactive<Record<string, any>>(createForm());
|
const catalogList = ref<Array<Record<string, any>>>([]);
|
||||||
|
const selectedCatalogKeys = ref<Array<number>>([]);
|
||||||
|
const gameForm = reactive<Record<string, any>>(createGameForm());
|
||||||
|
const providerForm = reactive<Record<string, any>>(createProviderForm());
|
||||||
|
|
||||||
const columns = [
|
const gameColumns = [
|
||||||
{ dataIndex: 'cover', key: 'cover', title: '封面', width: 90 },
|
{ dataIndex: 'cover', key: 'cover', title: '封面', width: 90 },
|
||||||
{ dataIndex: 'name', key: 'name', title: '名称', width: 180 },
|
{ dataIndex: 'name', key: 'name', title: '名称', width: 180 },
|
||||||
{ dataIndex: 'gameId', key: 'gameId', title: '内部ID', width: 150 },
|
{ dataIndex: 'gameId', key: 'gameId', title: '内部ID', width: 150 },
|
||||||
@ -137,37 +169,124 @@ const columns = [
|
|||||||
{ dataIndex: 'actions', key: 'actions', title: '操作', width: 160 },
|
{ dataIndex: 'actions', key: 'actions', title: '操作', width: 160 },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const catalogColumns = [
|
||||||
|
{ dataIndex: 'cover', key: 'cover', title: '封面', width: 90 },
|
||||||
|
{ dataIndex: 'name', key: 'name', title: '名称', width: 180 },
|
||||||
|
{ dataIndex: 'gameId', key: 'gameId', title: '内部ID', width: 150 },
|
||||||
|
{ dataIndex: 'vendorGameId', key: 'vendorGameId', title: '百顺ID', width: 120 },
|
||||||
|
{ dataIndex: 'status', key: 'status', title: '目录状态', width: 110 },
|
||||||
|
{ dataIndex: 'packageVersion', key: 'packageVersion', title: '包版本', width: 110 },
|
||||||
|
{ dataIndex: 'gsp', key: 'gsp', title: 'GSP', width: 90 },
|
||||||
|
{ dataIndex: 'added', key: 'added', title: '后台列表', width: 110 },
|
||||||
|
{ dataIndex: 'downloadUrl', key: 'downloadUrl', title: '入口地址', width: 360 },
|
||||||
|
{ dataIndex: 'updateTime', key: 'updateTime', title: '更新时间', width: 180 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const catalogRowSelection = computed(() => ({
|
||||||
|
selectedRowKeys: selectedCatalogKeys.value,
|
||||||
|
onChange: (keys: Array<number | string>) => {
|
||||||
|
selectedCatalogKeys.value = keys.map((item) => Number(item));
|
||||||
|
},
|
||||||
|
getCheckboxProps: (record: Record<string, any>) => ({
|
||||||
|
disabled: Boolean(record.added),
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
sysOriginOptions,
|
sysOriginOptions,
|
||||||
(options) => {
|
(options) => {
|
||||||
if (!query.sysOrigin) {
|
if (!contextQuery.sysOrigin) {
|
||||||
query.sysOrigin = String(options[0]?.value || '');
|
contextQuery.sysOrigin = String(options[0]?.value || '');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ immediate: true },
|
{ immediate: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => query.sysOrigin,
|
() => contextQuery.sysOrigin,
|
||||||
(value) => {
|
(value) => {
|
||||||
if (value) {
|
if (!value) {
|
||||||
void loadData(true);
|
return;
|
||||||
}
|
}
|
||||||
|
Object.assign(providerForm, createProviderForm(value));
|
||||||
|
selectedCatalogKeys.value = [];
|
||||||
|
void Promise.all([loadProviderConfig(), loadGames(true), loadCatalog(true)]);
|
||||||
},
|
},
|
||||||
{ immediate: true },
|
{ immediate: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
function resetForm() {
|
function resetGameForm() {
|
||||||
Object.assign(form, createForm(query.sysOrigin));
|
Object.assign(gameForm, createGameForm(contextQuery.sysOrigin));
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyProviderForm(record: Record<string, any>) {
|
||||||
|
Object.assign(providerForm, createProviderForm(contextQuery.sysOrigin), {
|
||||||
|
...record,
|
||||||
|
sysOrigin: contextQuery.sysOrigin,
|
||||||
|
appId: Number(record.appId || 0) || undefined,
|
||||||
|
gsp: Number(record.gsp || 101) || 101,
|
||||||
|
launchCodeTtlSeconds:
|
||||||
|
Number(record.launchCodeTtlSeconds || 300) || 300,
|
||||||
|
ssTokenTtlSeconds:
|
||||||
|
Number(record.ssTokenTtlSeconds || 86400) || 86400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadProviderConfig() {
|
||||||
|
if (!contextQuery.sysOrigin) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
providerLoading.value = true;
|
||||||
|
try {
|
||||||
|
const result = await getBaishunProviderConfig({
|
||||||
|
sysOrigin: contextQuery.sysOrigin,
|
||||||
|
});
|
||||||
|
applyProviderForm(result || {});
|
||||||
|
} finally {
|
||||||
|
providerLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function persistProviderConfig(showSuccess = true) {
|
||||||
|
if (
|
||||||
|
!contextQuery.sysOrigin ||
|
||||||
|
!providerForm.platformBaseUrl ||
|
||||||
|
!providerForm.appId ||
|
||||||
|
!providerForm.appChannel ||
|
||||||
|
!providerForm.appKey
|
||||||
|
) {
|
||||||
|
message.warning('请补全百顺平台地址、AppId、AppChannel、AppKey');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
providerSaving.value = true;
|
||||||
|
try {
|
||||||
|
const result = await saveBaishunProviderConfig({
|
||||||
|
...providerForm,
|
||||||
|
sysOrigin: contextQuery.sysOrigin,
|
||||||
|
appId: Number(providerForm.appId || 0),
|
||||||
|
gsp: Number(providerForm.gsp || 101),
|
||||||
|
launchCodeTtlSeconds: Number(providerForm.launchCodeTtlSeconds || 300),
|
||||||
|
ssTokenTtlSeconds: Number(providerForm.ssTokenTtlSeconds || 86400),
|
||||||
|
});
|
||||||
|
applyProviderForm(result || providerForm);
|
||||||
|
if (showSuccess) {
|
||||||
|
message.success('百顺配置已保存');
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
providerSaving.value = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function openCreate() {
|
function openCreate() {
|
||||||
resetForm();
|
resetGameForm();
|
||||||
modalOpen.value = true;
|
modalOpen.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function openEdit(record: Record<string, any>) {
|
function openEdit(record: Record<string, any>) {
|
||||||
Object.assign(form, createForm(query.sysOrigin), {
|
Object.assign(gameForm, createGameForm(contextQuery.sysOrigin), {
|
||||||
...record,
|
...record,
|
||||||
id: String(record.id || ''),
|
id: String(record.id || ''),
|
||||||
vendorGameId: Number(record.vendorGameId || 0) || undefined,
|
vendorGameId: Number(record.vendorGameId || 0) || undefined,
|
||||||
@ -175,50 +294,88 @@ function openEdit(record: Record<string, any>) {
|
|||||||
modalOpen.value = true;
|
modalOpen.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadData(reset = false) {
|
async function loadGames(reset = false) {
|
||||||
if (!query.sysOrigin) {
|
if (!contextQuery.sysOrigin) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (reset) {
|
if (reset) {
|
||||||
query.cursor = 1;
|
gameQuery.cursor = 1;
|
||||||
}
|
}
|
||||||
loading.value = true;
|
gameLoading.value = true;
|
||||||
try {
|
try {
|
||||||
const result = await pageBaishunGames({ ...query });
|
const result = await pageBaishunGames({
|
||||||
list.value = Array.isArray(result.records) ? result.records : [];
|
...gameQuery,
|
||||||
total.value = Number(result.total || 0);
|
sysOrigin: contextQuery.sysOrigin,
|
||||||
|
});
|
||||||
|
gameList.value = Array.isArray(result.records) ? result.records : [];
|
||||||
|
totalGames.value = Number(result.total || 0);
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
gameLoading.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSearch() {
|
async function loadCatalog(reset = false) {
|
||||||
void loadData(true);
|
if (!contextQuery.sysOrigin) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (reset) {
|
||||||
|
catalogQuery.cursor = 1;
|
||||||
|
}
|
||||||
|
catalogLoading.value = true;
|
||||||
|
try {
|
||||||
|
const result = await pageBaishunCatalog({
|
||||||
|
...catalogQuery,
|
||||||
|
sysOrigin: contextQuery.sysOrigin,
|
||||||
|
});
|
||||||
|
catalogList.value = Array.isArray(result.records) ? result.records : [];
|
||||||
|
totalCatalog.value = Number(result.total || 0);
|
||||||
|
} finally {
|
||||||
|
catalogLoading.value = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlePageChange(page: number, pageSize: number) {
|
function handleGameSearch() {
|
||||||
query.cursor = page;
|
void loadGames(true);
|
||||||
query.limit = pageSize;
|
|
||||||
void loadData();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submitForm() {
|
function handleCatalogSearch() {
|
||||||
if (!form.sysOrigin || !form.name || !form.gameId || !form.vendorGameId) {
|
void loadCatalog(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleGamePageChange(page: number, pageSize: number) {
|
||||||
|
gameQuery.cursor = page;
|
||||||
|
gameQuery.limit = pageSize;
|
||||||
|
void loadGames();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCatalogPageChange(page: number, pageSize: number) {
|
||||||
|
catalogQuery.cursor = page;
|
||||||
|
catalogQuery.limit = pageSize;
|
||||||
|
void loadCatalog();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submitGameForm() {
|
||||||
|
if (
|
||||||
|
!gameForm.sysOrigin ||
|
||||||
|
!gameForm.name ||
|
||||||
|
!gameForm.gameId ||
|
||||||
|
!gameForm.vendorGameId
|
||||||
|
) {
|
||||||
message.warning('请补全系统、名称、内部ID和百顺ID');
|
message.warning('请补全系统、名称、内部ID和百顺ID');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
saving.value = true;
|
gameSaving.value = true;
|
||||||
try {
|
try {
|
||||||
await saveBaishunGame({
|
await saveBaishunGame({
|
||||||
...form,
|
...gameForm,
|
||||||
id: Number(form.id || 0),
|
id: Number(gameForm.id || 0),
|
||||||
vendorGameId: Number(form.vendorGameId || 0),
|
vendorGameId: Number(gameForm.vendorGameId || 0),
|
||||||
});
|
});
|
||||||
message.success('保存成功');
|
message.success('保存成功');
|
||||||
modalOpen.value = false;
|
modalOpen.value = false;
|
||||||
await loadData(true);
|
await Promise.all([loadGames(true), loadCatalog(true)]);
|
||||||
} finally {
|
} finally {
|
||||||
saving.value = false;
|
gameSaving.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,207 +385,391 @@ function handleDelete(record: Record<string, any>) {
|
|||||||
async onOk() {
|
async onOk() {
|
||||||
await deleteBaishunGame(record.id, record.sysOrigin);
|
await deleteBaishunGame(record.id, record.sysOrigin);
|
||||||
message.success('删除成功');
|
message.success('删除成功');
|
||||||
await loadData(true);
|
await Promise.all([loadGames(true), loadCatalog(true)]);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleImportCatalog() {
|
async function handleSaveProviderConfig() {
|
||||||
if (!query.sysOrigin) {
|
await persistProviderConfig(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleFetchCatalog() {
|
||||||
|
if (!(await persistProviderConfig(false))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
importing.value = true;
|
catalogFetching.value = true;
|
||||||
try {
|
try {
|
||||||
const result = await importBaishunCatalog({ sysOrigin: query.sysOrigin });
|
const result = await fetchBaishunCatalog({
|
||||||
message.success(`导入完成:新增 ${Number(result.imported || 0)},已存在 ${Number(result.existing || 0)}`);
|
sysOrigin: contextQuery.sysOrigin,
|
||||||
await loadData(true);
|
});
|
||||||
|
message.success(
|
||||||
|
`获取完成:新增 ${Number(result.inserted || 0)},更新 ${Number(result.updated || 0)},跳过 ${Number(result.skipped || 0)}`,
|
||||||
|
);
|
||||||
|
activeTab.value = 'catalog';
|
||||||
|
await loadCatalog(true);
|
||||||
} finally {
|
} finally {
|
||||||
importing.value = false;
|
catalogFetching.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleSync() {
|
async function handleImportSelected() {
|
||||||
if (!query.sysOrigin) {
|
if (!contextQuery.sysOrigin) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
syncing.value = true;
|
if (selectedCatalogKeys.value.length === 0) {
|
||||||
|
message.warning('请选择要添加到后台列表的百顺游戏');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catalogImporting.value = true;
|
||||||
try {
|
try {
|
||||||
const result = await syncBaishunGames({ sysOrigin: query.sysOrigin });
|
const result = await importBaishunCatalog({
|
||||||
|
sysOrigin: contextQuery.sysOrigin,
|
||||||
|
vendorGameIds: selectedCatalogKeys.value,
|
||||||
|
});
|
||||||
|
selectedCatalogKeys.value = [];
|
||||||
message.success(
|
message.success(
|
||||||
`同步完成:目录新增 ${Number(result.inserted || 0)},更新 ${Number(result.updated || 0)},导入 ${Number(result.imported || 0)}`,
|
`添加完成:新增 ${Number(result.imported || 0)},已存在 ${Number(result.existing || 0)}`,
|
||||||
);
|
);
|
||||||
await loadData(true);
|
await Promise.all([loadGames(true), loadCatalog(true)]);
|
||||||
} finally {
|
} finally {
|
||||||
syncing.value = false;
|
catalogImporting.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Page title="百顺游戏管理">
|
<Page title="三方-百顺">
|
||||||
<Card>
|
<Card :loading="providerLoading" title="百顺接入配置">
|
||||||
<div class="toolbar">
|
<Form layout="vertical">
|
||||||
<SysOriginSelect
|
<div class="form-grid provider-grid">
|
||||||
v-model:value="query.sysOrigin"
|
<FormItem label="系统">
|
||||||
style="width: 140px"
|
<SysOriginSelect
|
||||||
:options="sysOriginOptions"
|
v-model:value="contextQuery.sysOrigin"
|
||||||
/>
|
:options="sysOriginOptions"
|
||||||
<Select
|
/>
|
||||||
v-model:value="query.showcase"
|
</FormItem>
|
||||||
style="width: 120px"
|
<FormItem
|
||||||
:options="showcaseOptions"
|
label="平台地址"
|
||||||
@change="handleSearch"
|
extra="填写百顺平台接口地址,例如 https://game-cn-test.jieyou.shop;不要填 Lucky Gift 域名。"
|
||||||
/>
|
>
|
||||||
<Input
|
<Input
|
||||||
v-model:value="query.keyword"
|
v-model:value="providerForm.platformBaseUrl"
|
||||||
placeholder="搜索名称 / 内部ID / 百顺ID"
|
placeholder="https://game-cn-test.jieyou.shop"
|
||||||
style="width: 240px"
|
/>
|
||||||
@pressEnter="handleSearch"
|
</FormItem>
|
||||||
/>
|
<FormItem label="AppId">
|
||||||
<Button type="primary" :loading="loading" @click="handleSearch">
|
<InputNumber
|
||||||
搜索
|
v-model:value="providerForm.appId"
|
||||||
</Button>
|
:min="1"
|
||||||
<Button :loading="importing" @click="handleImportCatalog">
|
style="width: 100%"
|
||||||
导入本地目录
|
/>
|
||||||
</Button>
|
</FormItem>
|
||||||
<Button :loading="syncing" @click="handleSync">
|
<FormItem label="AppName">
|
||||||
同步百顺目录
|
<Input v-model:value="providerForm.appName" placeholder="可选" />
|
||||||
</Button>
|
</FormItem>
|
||||||
<Button type="primary" @click="openCreate">
|
<FormItem label="AppChannel">
|
||||||
新增
|
<Input
|
||||||
</Button>
|
v-model:value="providerForm.appChannel"
|
||||||
</div>
|
placeholder="yumiparty"
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem label="AppKey">
|
||||||
|
<Input
|
||||||
|
v-model:value="providerForm.appKey"
|
||||||
|
placeholder="请输入百顺密钥"
|
||||||
|
type="password"
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem label="默认 GSP">
|
||||||
|
<InputNumber
|
||||||
|
v-model:value="providerForm.gsp"
|
||||||
|
:min="1"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem label="LaunchCode TTL(秒)">
|
||||||
|
<InputNumber
|
||||||
|
v-model:value="providerForm.launchCodeTtlSeconds"
|
||||||
|
:min="1"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem label="SsToken TTL(秒)">
|
||||||
|
<InputNumber
|
||||||
|
v-model:value="providerForm.ssTokenTtlSeconds"
|
||||||
|
:min="1"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
</div>
|
||||||
|
<div class="toolbar">
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
:loading="providerSaving"
|
||||||
|
@click="handleSaveProviderConfig"
|
||||||
|
>
|
||||||
|
保存配置
|
||||||
|
</Button>
|
||||||
|
<Button :loading="catalogFetching" @click="handleFetchCatalog">
|
||||||
|
获取百顺游戏列表
|
||||||
|
</Button>
|
||||||
|
<span class="hint">
|
||||||
|
房间页只读取“已添加游戏”,目录页只负责预览和选择导入。
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
</Card>
|
||||||
|
|
||||||
<Table
|
<Card class="content-card">
|
||||||
:columns="columns"
|
<Tabs v-model:activeKey="activeTab">
|
||||||
:data-source="list"
|
<TabPane key="configured" tab="已添加游戏">
|
||||||
:loading="loading"
|
<div class="toolbar">
|
||||||
:pagination="false"
|
<Select
|
||||||
row-key="id"
|
v-model:value="gameQuery.showcase"
|
||||||
:scroll="{ x: 1600 }"
|
style="width: 120px"
|
||||||
>
|
:options="showcaseOptions"
|
||||||
<template #bodyCell="{ column, record }">
|
@change="handleGameSearch"
|
||||||
<template v-if="column.key === 'cover'">
|
/>
|
||||||
<Image v-if="record.cover" :src="record.cover" class="cover" />
|
<Input
|
||||||
<span v-else>-</span>
|
v-model:value="gameQuery.keyword"
|
||||||
</template>
|
placeholder="搜索名称 / 内部ID / 百顺ID"
|
||||||
<template v-else-if="column.key === 'showcase'">
|
style="width: 240px"
|
||||||
<Tag :color="record.showcase ? 'success' : 'default'">
|
@pressEnter="handleGameSearch"
|
||||||
{{ record.showcase ? '上架' : '下架' }}
|
/>
|
||||||
</Tag>
|
<Button type="primary" :loading="gameLoading" @click="handleGameSearch">
|
||||||
</template>
|
搜索
|
||||||
<template v-else-if="column.key === 'actions'">
|
</Button>
|
||||||
<Space>
|
<Button type="primary" @click="openCreate">
|
||||||
<Button size="small" type="link" @click="openEdit(record)">
|
新增
|
||||||
编辑
|
</Button>
|
||||||
</Button>
|
</div>
|
||||||
<Button danger size="small" type="link" @click="handleDelete(record)">
|
|
||||||
删除
|
|
||||||
</Button>
|
|
||||||
</Space>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</Table>
|
|
||||||
|
|
||||||
<div v-if="total > 0" class="pager">
|
<Table
|
||||||
<Pagination
|
:columns="gameColumns"
|
||||||
:current="query.cursor"
|
:data-source="gameList"
|
||||||
:page-size="query.limit"
|
:loading="gameLoading"
|
||||||
:total="total"
|
:pagination="false"
|
||||||
show-size-changer
|
row-key="id"
|
||||||
@change="handlePageChange"
|
:scroll="{ x: 1600 }"
|
||||||
@showSizeChange="handlePageChange"
|
>
|
||||||
/>
|
<template #bodyCell="{ column, record }">
|
||||||
</div>
|
<template v-if="column.key === 'cover'">
|
||||||
|
<Image v-if="record.cover" :src="record.cover" class="cover" />
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.key === 'showcase'">
|
||||||
|
<Tag :color="record.showcase ? 'success' : 'default'">
|
||||||
|
{{ record.showcase ? '上架' : '下架' }}
|
||||||
|
</Tag>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.key === 'actions'">
|
||||||
|
<Space>
|
||||||
|
<Button size="small" type="link" @click="openEdit(record)">
|
||||||
|
编辑
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
danger
|
||||||
|
size="small"
|
||||||
|
type="link"
|
||||||
|
@click="handleDelete(record)"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</Table>
|
||||||
|
|
||||||
|
<div v-if="totalGames > 0" class="pager">
|
||||||
|
<Pagination
|
||||||
|
:current="gameQuery.cursor"
|
||||||
|
:page-size="gameQuery.limit"
|
||||||
|
:total="totalGames"
|
||||||
|
show-size-changer
|
||||||
|
@change="handleGamePageChange"
|
||||||
|
@showSizeChange="handleGamePageChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</TabPane>
|
||||||
|
|
||||||
|
<TabPane key="catalog" tab="百顺目录">
|
||||||
|
<div class="toolbar">
|
||||||
|
<Input
|
||||||
|
v-model:value="catalogQuery.keyword"
|
||||||
|
placeholder="搜索目录名称 / 内部ID / 百顺ID"
|
||||||
|
style="width: 260px"
|
||||||
|
@pressEnter="handleCatalogSearch"
|
||||||
|
/>
|
||||||
|
<Button :loading="catalogLoading" type="primary" @click="handleCatalogSearch">
|
||||||
|
搜索
|
||||||
|
</Button>
|
||||||
|
<Button :loading="catalogFetching" @click="handleFetchCatalog">
|
||||||
|
重新获取目录
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
:loading="catalogImporting"
|
||||||
|
@click="handleImportSelected"
|
||||||
|
>
|
||||||
|
添加已选游戏
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Table
|
||||||
|
:columns="catalogColumns"
|
||||||
|
:data-source="catalogList"
|
||||||
|
:loading="catalogLoading"
|
||||||
|
:pagination="false"
|
||||||
|
row-key="vendorGameId"
|
||||||
|
:row-selection="catalogRowSelection"
|
||||||
|
:scroll="{ x: 1700 }"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.key === 'cover'">
|
||||||
|
<Image v-if="record.cover" :src="record.cover" class="cover" />
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.key === 'status'">
|
||||||
|
<Tag :color="record.status === 'ENABLED' ? 'success' : 'default'">
|
||||||
|
{{ record.status || '-' }}
|
||||||
|
</Tag>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.key === 'added'">
|
||||||
|
<Tag :color="record.added ? 'success' : 'default'">
|
||||||
|
{{ record.added ? (record.showcase ? '已添加-上架' : '已添加-下架') : '未添加' }}
|
||||||
|
</Tag>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.key === 'downloadUrl'">
|
||||||
|
<a
|
||||||
|
v-if="record.downloadUrl"
|
||||||
|
:href="record.downloadUrl"
|
||||||
|
class="link-cell"
|
||||||
|
rel="noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{{ record.downloadUrl }}
|
||||||
|
</a>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</Table>
|
||||||
|
|
||||||
|
<div v-if="totalCatalog > 0" class="pager">
|
||||||
|
<Pagination
|
||||||
|
:current="catalogQuery.cursor"
|
||||||
|
:page-size="catalogQuery.limit"
|
||||||
|
:total="totalCatalog"
|
||||||
|
show-size-changer
|
||||||
|
@change="handleCatalogPageChange"
|
||||||
|
@showSizeChange="handleCatalogPageChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</TabPane>
|
||||||
|
</Tabs>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
:confirm-loading="saving"
|
:confirm-loading="gameSaving"
|
||||||
:open="modalOpen"
|
:open="modalOpen"
|
||||||
destroy-on-close
|
destroy-on-close
|
||||||
:title="form.id ? '编辑百顺游戏' : '新增百顺游戏'"
|
:title="gameForm.id ? '编辑百顺游戏' : '新增百顺游戏'"
|
||||||
width="860px"
|
width="860px"
|
||||||
@cancel="modalOpen = false"
|
@cancel="modalOpen = false"
|
||||||
@ok="submitForm"
|
@ok="submitGameForm"
|
||||||
>
|
>
|
||||||
<Form layout="vertical">
|
<Form layout="vertical">
|
||||||
<div class="form-grid">
|
<div class="form-grid">
|
||||||
<FormItem label="系统">
|
<FormItem label="系统">
|
||||||
<SysOriginSelect v-model:value="form.sysOrigin" :options="sysOriginOptions" />
|
<SysOriginSelect
|
||||||
|
v-model:value="gameForm.sysOrigin"
|
||||||
|
:options="sysOriginOptions"
|
||||||
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="名称">
|
<FormItem label="名称">
|
||||||
<Input v-model:value="form.name" placeholder="请输入游戏名称" />
|
<Input v-model:value="gameForm.name" placeholder="请输入游戏名称" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="内部ID">
|
<FormItem label="内部ID">
|
||||||
<Input v-model:value="form.gameId" placeholder="例如 bs_1100" />
|
<Input v-model:value="gameForm.gameId" placeholder="例如 bs_1100" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="百顺ID">
|
<FormItem label="百顺ID">
|
||||||
<InputNumber v-model:value="form.vendorGameId" :min="1" style="width: 100%" />
|
<InputNumber
|
||||||
|
v-model:value="gameForm.vendorGameId"
|
||||||
|
:min="1"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="编号">
|
<FormItem label="编号">
|
||||||
<Input v-model:value="form.gameCode" placeholder="默认取百顺ID" />
|
<Input v-model:value="gameForm.gameCode" placeholder="默认取百顺ID" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="分类">
|
<FormItem label="分类">
|
||||||
<Select v-model:value="form.category" :options="categoryOptions" />
|
<Select v-model:value="gameForm.category" :options="categoryOptions" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="客户端">
|
<FormItem label="客户端">
|
||||||
<Select v-model:value="form.clientOrigin" :options="clientOriginOptions" />
|
<Select
|
||||||
|
v-model:value="gameForm.clientOrigin"
|
||||||
|
:options="clientOriginOptions"
|
||||||
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="状态">
|
<FormItem label="状态">
|
||||||
<Select v-model:value="form.showcase" :options="booleanOptions" />
|
<Select v-model:value="gameForm.showcase" :options="booleanOptions" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="是否全屏">
|
<FormItem label="是否全屏">
|
||||||
<Select v-model:value="form.fullScreen" :options="booleanOptions" />
|
<Select v-model:value="gameForm.fullScreen" :options="booleanOptions" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="Game Mode">
|
<FormItem label="Game Mode">
|
||||||
<Select v-model:value="form.gameMode" :options="gameModeOptions" />
|
<Select v-model:value="gameForm.gameMode" :options="gameModeOptions" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="启动模式">
|
<FormItem label="启动模式">
|
||||||
<Select v-model:value="form.launchMode" :options="launchModeOptions" />
|
<Select v-model:value="gameForm.launchMode" :options="launchModeOptions" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="排序">
|
<FormItem label="排序">
|
||||||
<InputNumber v-model:value="form.sort" :min="0" style="width: 100%" />
|
<InputNumber v-model:value="gameForm.sort" :min="0" style="width: 100%" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="安全区高度">
|
<FormItem label="安全区高度">
|
||||||
<InputNumber v-model:value="form.safeHeight" :min="0" style="width: 100%" />
|
<InputNumber
|
||||||
|
v-model:value="gameForm.safeHeight"
|
||||||
|
:min="0"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="Orientation">
|
<FormItem label="Orientation">
|
||||||
<Select
|
<Select
|
||||||
v-model:value="form.orientation"
|
v-model:value="gameForm.orientation"
|
||||||
:options="orientationOptions"
|
:options="orientationOptions"
|
||||||
allow-clear
|
allow-clear
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="宽">
|
<FormItem label="宽">
|
||||||
<InputNumber v-model:value="form.width" :min="0" style="width: 100%" />
|
<InputNumber v-model:value="gameForm.width" :min="0" style="width: 100%" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="高">
|
<FormItem label="高">
|
||||||
<InputNumber v-model:value="form.height" :min="0" style="width: 100%" />
|
<InputNumber v-model:value="gameForm.height" :min="0" style="width: 100%" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="GSP">
|
<FormItem label="GSP">
|
||||||
<Input v-model:value="form.gsp" placeholder="默认取目录或系统配置" />
|
<Input v-model:value="gameForm.gsp" placeholder="默认取系统百顺配置" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="金额">
|
<FormItem label="金额">
|
||||||
<Input v-model:value="form.amounts" placeholder="可选" />
|
<Input v-model:value="gameForm.amounts" placeholder="可选" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="区域">
|
<FormItem label="区域">
|
||||||
<Input v-model:value="form.regions" placeholder="多个区域用逗号分隔" />
|
<Input v-model:value="gameForm.regions" placeholder="多个区域用逗号分隔" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="货币图标">
|
<FormItem label="货币图标">
|
||||||
<Input v-model:value="form.currencyIcon" placeholder="可选" />
|
<Input v-model:value="gameForm.currencyIcon" placeholder="可选" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</div>
|
</div>
|
||||||
<FormItem label="封面">
|
<FormItem label="封面">
|
||||||
<Input v-model:value="form.cover" placeholder="请输入封面 URL" />
|
<Input v-model:value="gameForm.cover" placeholder="请输入封面 URL" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="预览地址">
|
<FormItem label="预览地址">
|
||||||
<Input v-model:value="form.previewUrl" placeholder="请输入预览地址" />
|
<Input v-model:value="gameForm.previewUrl" placeholder="请输入预览地址" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="包地址">
|
<FormItem label="包地址">
|
||||||
<Input v-model:value="form.packageUrl" placeholder="请输入包地址" />
|
<Input v-model:value="gameForm.packageUrl" placeholder="请输入包地址" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="包版本">
|
<FormItem label="包版本">
|
||||||
<Input v-model:value="form.packageVersion" placeholder="请输入包版本" />
|
<Input v-model:value="gameForm.packageVersion" placeholder="请输入包版本" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</Form>
|
</Form>
|
||||||
</Modal>
|
</Modal>
|
||||||
@ -436,6 +777,10 @@ async function handleSync() {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.content-card {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
.toolbar {
|
.toolbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@ -462,8 +807,33 @@ async function handleSync() {
|
|||||||
gap: 0 16px;
|
gap: 0 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.provider-grid {
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.hint {
|
||||||
|
color: rgb(100 116 139);
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-cell {
|
||||||
|
display: inline-block;
|
||||||
|
max-width: 340px;
|
||||||
|
overflow: hidden;
|
||||||
|
color: rgb(37 99 235);
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1200px) {
|
||||||
|
.provider-grid {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 900px) {
|
@media (max-width: 900px) {
|
||||||
.form-grid {
|
.form-grid,
|
||||||
|
.provider-grid {
|
||||||
grid-template-columns: minmax(0, 1fr);
|
grid-template-columns: minmax(0, 1fr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
computed,
|
computed,
|
||||||
reactive,
|
reactive,
|
||||||
@ -12,7 +14,6 @@ import { useAccessStore } from '@vben/stores';
|
|||||||
|
|
||||||
import { getCountryAlls } from '#/api/legacy/system';
|
import { getCountryAlls } from '#/api/legacy/system';
|
||||||
import {
|
import {
|
||||||
getUserBaseInfoByAccount,
|
|
||||||
pageUserBaseInfo,
|
pageUserBaseInfo,
|
||||||
} from '#/api/legacy/user';
|
} from '#/api/legacy/user';
|
||||||
import { getAllowedSysOrigins } from '#/views/system/shared';
|
import { getAllowedSysOrigins } from '#/views/system/shared';
|
||||||
@ -44,10 +45,14 @@ import {
|
|||||||
REGISTER_ORIGIN_OPTIONS,
|
REGISTER_ORIGIN_OPTIONS,
|
||||||
USER_TYPE_OPTIONS,
|
USER_TYPE_OPTIONS,
|
||||||
} from './constants';
|
} from './constants';
|
||||||
import UserProfileLink from './components/user-profile-link.vue';
|
|
||||||
import UserAuthInfoDrawer from './components/user-auth-info-drawer.vue';
|
import UserAuthInfoDrawer from './components/user-auth-info-drawer.vue';
|
||||||
import UserBalanceHandleModal from './components/user-balance-handle-modal.vue';
|
import UserBalanceHandleModal from './components/user-balance-handle-modal.vue';
|
||||||
import UserResetPasswordModal from './components/user-reset-password-modal.vue';
|
import UserResetPasswordModal from './components/user-reset-password-modal.vue';
|
||||||
|
import {
|
||||||
|
getUserProfileAvatar,
|
||||||
|
getUserProfileId,
|
||||||
|
getUserProfileName,
|
||||||
|
} from './shared';
|
||||||
|
|
||||||
defineOptions({ name: 'OperateUserManage' });
|
defineOptions({ name: 'OperateUserManage' });
|
||||||
|
|
||||||
@ -114,7 +119,6 @@ const columns = [
|
|||||||
},
|
},
|
||||||
{ dataIndex: 'accountStatus', key: 'accountStatus', title: '账户', width: 120 },
|
{ dataIndex: 'accountStatus', key: 'accountStatus', title: '账户', width: 120 },
|
||||||
{ dataIndex: 'registerSource', key: 'registerSource', title: '注册来源', width: 180 },
|
{ dataIndex: 'registerSource', key: 'registerSource', title: '注册来源', width: 180 },
|
||||||
{ dataIndex: 'ids', key: 'ids', title: '编号', width: 180 },
|
|
||||||
{ dataIndex: 'time', key: 'time', title: '时间', width: 220 },
|
{ dataIndex: 'time', key: 'time', title: '时间', width: 220 },
|
||||||
{ dataIndex: 'actions', key: 'actions', title: '操作', width: 320 },
|
{ dataIndex: 'actions', key: 'actions', title: '操作', width: 320 },
|
||||||
];
|
];
|
||||||
@ -245,6 +249,21 @@ function openUserDetails(userId?: number | string) {
|
|||||||
router.push(`/common/user/deatils/${userId}`);
|
router.push(`/common/user/deatils/${userId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getUserShortId(profile?: Record<string, any> | null) {
|
||||||
|
return profile?.actualAccount || profile?.account || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatListDate(value?: number | string) {
|
||||||
|
if (!value) {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
const date = dayjs(value);
|
||||||
|
if (!date.isValid()) {
|
||||||
|
return String(value);
|
||||||
|
}
|
||||||
|
return date.format('YYYY MM DD');
|
||||||
|
}
|
||||||
|
|
||||||
function openEdit(record: Record<string, any>) {
|
function openEdit(record: Record<string, any>) {
|
||||||
activeUserId.value = record.userProfile?.id || '';
|
activeUserId.value = record.userProfile?.id || '';
|
||||||
editOpen.value = true;
|
editOpen.value = true;
|
||||||
@ -330,19 +349,6 @@ async function copyUserId(value?: number | string) {
|
|||||||
message.success('复制成功');
|
message.success('复制成功');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openByShortAccount(account?: number | string) {
|
|
||||||
if (!account) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const result = await getUserBaseInfoByAccount(account);
|
|
||||||
const userId = result?.id || result?.userId;
|
|
||||||
if (!userId) {
|
|
||||||
message.warning('未找到用户');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
openUserDetails(userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
void loadCountries();
|
void loadCountries();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -424,11 +430,48 @@ void loadCountries();
|
|||||||
:loading="loading"
|
:loading="loading"
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
row-key="id"
|
row-key="id"
|
||||||
:scroll="{ x: 1760 }"
|
:scroll="{ x: 1580 }"
|
||||||
>
|
>
|
||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
<template v-if="column.key === 'userProfile'">
|
<template v-if="column.key === 'userProfile'">
|
||||||
<UserProfileLink :profile="record.userProfile" />
|
<div class="user-cell">
|
||||||
|
<button
|
||||||
|
v-if="getUserProfileId(record.userProfile)"
|
||||||
|
type="button"
|
||||||
|
class="user-cell__avatar-button"
|
||||||
|
@click="openUserDetails(getUserProfileId(record.userProfile))"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
:src="getUserProfileAvatar(record.userProfile) || 'https://dummyimage.com/56x56/e2e8f0/64748b&text=U'"
|
||||||
|
alt=""
|
||||||
|
class="user-cell__avatar"
|
||||||
|
>
|
||||||
|
</button>
|
||||||
|
<div v-else class="user-cell__avatar-shell">
|
||||||
|
<img
|
||||||
|
:src="getUserProfileAvatar(record.userProfile) || 'https://dummyimage.com/56x56/e2e8f0/64748b&text=U'"
|
||||||
|
alt=""
|
||||||
|
class="user-cell__avatar"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="user-cell__info">
|
||||||
|
<div class="user-cell__name">{{ getUserProfileName(record.userProfile) }}</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="user-cell__copy"
|
||||||
|
@click="copyUserId(getUserProfileId(record.userProfile))"
|
||||||
|
>
|
||||||
|
ID:{{ getUserProfileId(record.userProfile) || '-' }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="user-cell__copy"
|
||||||
|
@click="copyUserId(getUserShortId(record.userProfile))"
|
||||||
|
>
|
||||||
|
短ID:{{ getUserShortId(record.userProfile) || '-' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="column.key === 'goldBalance'">
|
<template v-else-if="column.key === 'goldBalance'">
|
||||||
<Button size="small" type="link" @click="openRunningWater(record, 'gold')">
|
<Button size="small" type="link" @click="openRunningWater(record, 'gold')">
|
||||||
@ -451,19 +494,9 @@ void loadCountries();
|
|||||||
<template v-else-if="column.key === 'registerSource'">
|
<template v-else-if="column.key === 'registerSource'">
|
||||||
{{ resolveRegisterSource(record) }}
|
{{ resolveRegisterSource(record) }}
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="column.key === 'ids'">
|
|
||||||
<div class="id-cell">
|
|
||||||
<button type="button" @click="copyUserId(record.userProfile?.id)">
|
|
||||||
用户ID:{{ record.userProfile?.id || '-' }}
|
|
||||||
</button>
|
|
||||||
<button type="button" @click="openByShortAccount(record.userProfile?.actualAccount)">
|
|
||||||
短账号:{{ record.userProfile?.actualAccount || '-' }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template v-else-if="column.key === 'time'">
|
<template v-else-if="column.key === 'time'">
|
||||||
<div>创建时间:{{ record.userProfile?.createTime || '-' }}</div>
|
<div>创建时间:{{ formatListDate(record.userProfile?.createTime) }}</div>
|
||||||
<div>最近活跃:{{ record.lastActiveTime || '-' }}</div>
|
<div>最近活跃:{{ formatListDate(record.lastActiveTime) }}</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="column.key === 'actions'">
|
<template v-else-if="column.key === 'actions'">
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
@ -582,12 +615,48 @@ void loadCountries();
|
|||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.id-cell {
|
.user-cell {
|
||||||
display: grid;
|
align-items: center;
|
||||||
gap: 4px;
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.id-cell button {
|
.user-cell__avatar-button,
|
||||||
|
.user-cell__avatar-shell {
|
||||||
|
align-items: center;
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
display: inline-flex;
|
||||||
|
flex-shrink: 0;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-cell__avatar-button {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-cell__avatar {
|
||||||
|
background: #e2e8f0;
|
||||||
|
border-radius: 999px;
|
||||||
|
height: 40px;
|
||||||
|
object-fit: cover;
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-cell__info {
|
||||||
|
display: grid;
|
||||||
|
gap: 4px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-cell__name {
|
||||||
|
color: #0f172a;
|
||||||
|
font-weight: 600;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-cell__copy {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: 0;
|
border: 0;
|
||||||
color: #2563eb;
|
color: #2563eb;
|
||||||
|
|||||||
@ -179,9 +179,14 @@ loadData(true);
|
|||||||
class="user-avatar"
|
class="user-avatar"
|
||||||
>
|
>
|
||||||
<div class="user-info">
|
<div class="user-info">
|
||||||
<div>{{ record.userProfile?.userNickname || '-' }}</div>
|
<div class="user-name-row">
|
||||||
|
<span>{{ record.userProfile?.userNickname || '-' }}</span>
|
||||||
|
<span class="user-short-id">
|
||||||
|
短ID:{{ record.userProfile?.actualAccount || '-' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<div class="user-sub">
|
<div class="user-sub">
|
||||||
{{ record.userProfile?.actualAccount || record.userProfile?.id || '-' }}
|
ID:{{ record.userProfile?.id || '-' }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -271,6 +276,18 @@ loadData(true);
|
|||||||
width: 44px;
|
width: 44px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.user-name-row {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-short-id {
|
||||||
|
color: #64748b;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.user-sub {
|
.user-sub {
|
||||||
color: #64748b;
|
color: #64748b;
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
|
|||||||
52
scripts/sql/sync_team_business_development_menu.sql
Normal file
52
scripts/sql/sync_team_business_development_menu.sql
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
INSERT INTO sys_menu (
|
||||||
|
id,
|
||||||
|
parent_id,
|
||||||
|
menu_name,
|
||||||
|
path,
|
||||||
|
menu_type,
|
||||||
|
icon,
|
||||||
|
create_user,
|
||||||
|
update_user,
|
||||||
|
sort,
|
||||||
|
status,
|
||||||
|
router,
|
||||||
|
alias
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
1700000088,
|
||||||
|
(SELECT id FROM sys_menu WHERE alias = 'TeamManager' LIMIT 1),
|
||||||
|
'BD列表',
|
||||||
|
'team/business-development/index',
|
||||||
|
2,
|
||||||
|
'form',
|
||||||
|
'0',
|
||||||
|
'0',
|
||||||
|
241,
|
||||||
|
0,
|
||||||
|
'room/business-development',
|
||||||
|
'BusinessDevelopment'
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1 FROM sys_menu WHERE alias = 'BusinessDevelopment'
|
||||||
|
);
|
||||||
|
|
||||||
|
UPDATE sys_menu
|
||||||
|
SET
|
||||||
|
parent_id = (
|
||||||
|
SELECT team_menu.id
|
||||||
|
FROM (
|
||||||
|
SELECT id
|
||||||
|
FROM sys_menu
|
||||||
|
WHERE alias = 'TeamManager'
|
||||||
|
LIMIT 1
|
||||||
|
) AS team_menu
|
||||||
|
),
|
||||||
|
menu_name = 'BD列表',
|
||||||
|
path = 'team/business-development/index',
|
||||||
|
menu_type = 2,
|
||||||
|
icon = 'form',
|
||||||
|
create_user = '0',
|
||||||
|
update_user = '0',
|
||||||
|
sort = 241,
|
||||||
|
status = 0,
|
||||||
|
router = 'room/business-development'
|
||||||
|
WHERE alias = 'BusinessDevelopment';
|
||||||
Loading…
x
Reference in New Issue
Block a user