feat: sync admin local updates
This commit is contained in:
parent
edb43e2532
commit
44531d62bb
@ -241,9 +241,9 @@ export async function removeSpecialId(data: Record<string, any>) {
|
||||
return requestClient.post('/user-special-id/remove', data);
|
||||
}
|
||||
|
||||
export async function listSpecialIdLatestLog(account: number | string) {
|
||||
export async function listSpecialIdLatestLog(userId: number | string) {
|
||||
return requestClient.get<LegacySpecialIdLogItem[]>('/user-special-id/latest-log', {
|
||||
params: { account },
|
||||
params: { userId },
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
28
apps/src/api/legacy/baishun-game.ts
Normal file
28
apps/src/api/legacy/baishun-game.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export async function pageBaishunGames(params: Record<string, any>) {
|
||||
return requestClient.get<Record<string, any>>('/go/operate/baishun-game/page', {
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
export async function saveBaishunGame(data: Record<string, any>) {
|
||||
return requestClient.post<Record<string, any>>('/go/operate/baishun-game/save', data);
|
||||
}
|
||||
|
||||
export async function deleteBaishunGame(id: number | string, sysOrigin: string) {
|
||||
return requestClient.delete<Record<string, any>>('/go/operate/baishun-game', {
|
||||
params: { id, sysOrigin },
|
||||
});
|
||||
}
|
||||
|
||||
export async function importBaishunCatalog(data: Record<string, any>) {
|
||||
return requestClient.post<Record<string, any>>(
|
||||
'/go/operate/baishun-game/import-catalog',
|
||||
data,
|
||||
);
|
||||
}
|
||||
|
||||
export async function syncBaishunGames(data: Record<string, any>) {
|
||||
return requestClient.post<Record<string, any>>('/go/operate/baishun-game/sync', data);
|
||||
}
|
||||
@ -4,6 +4,7 @@ import { requestClient } from '#/api/request';
|
||||
|
||||
const SPECIFIED_GIFT_WEEKLY_RANK_BASE =
|
||||
'/go/resident-activity/specified-gift-weekly-rank';
|
||||
const SIGN_IN_REWARD_BASE = '/go/resident-activity/sign-in-reward';
|
||||
|
||||
export async function getResidentInviteConfig(sysOrigin: string) {
|
||||
return requestClient.get<Record<string, any>>('/resident-activity/invite/config', {
|
||||
@ -54,6 +55,22 @@ export async function pageResidentRegisterRewardDailyRecords(
|
||||
);
|
||||
}
|
||||
|
||||
export async function getResidentSignInRewardConfig(sysOrigin: string) {
|
||||
return requestClient.get<Record<string, any>>(`${SIGN_IN_REWARD_BASE}/config`, {
|
||||
params: { sysOrigin },
|
||||
});
|
||||
}
|
||||
|
||||
export async function saveResidentSignInRewardConfig(data: Record<string, any>) {
|
||||
return requestClient.post(`${SIGN_IN_REWARD_BASE}/config/save`, data);
|
||||
}
|
||||
|
||||
export async function pageResidentSignInRewardRecords(params: Record<string, any>) {
|
||||
return requestClient.get<Record<string, any>>(`${SIGN_IN_REWARD_BASE}/record/page`, {
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
export async function pageSpecifiedGiftWeeklyRankConfigs(params: Record<string, any>) {
|
||||
return requestClient.get<LegacyPageResult<Record<string, any>>>(
|
||||
`${SPECIFIED_GIFT_WEEKLY_RANK_BASE}/config/page`,
|
||||
|
||||
@ -16,9 +16,7 @@ const externalRouteFiles = import.meta.glob('./external/**/*.ts', {
|
||||
|
||||
const accessRouteFiles = Object.fromEntries(
|
||||
Object.entries(dynamicRouteFiles).filter(
|
||||
([path]) =>
|
||||
!path.endsWith('/dashboard.ts') &&
|
||||
!path.endsWith('/resident-activity.ts'),
|
||||
([path]) => !path.endsWith('/dashboard.ts'),
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@ -203,6 +203,12 @@ const routes: RouteRecordRaw[] = [
|
||||
component: () => import('#/views/operate/game-config.vue'),
|
||||
meta: { title: '游戏列表' },
|
||||
},
|
||||
{
|
||||
name: 'OperateBaishunGameManage',
|
||||
path: 'baishun-game-manage',
|
||||
component: () => import('#/views/operate/baishun-game-manage.vue'),
|
||||
meta: { title: '百顺游戏管理' },
|
||||
},
|
||||
{
|
||||
name: 'OperateMikeTypeList',
|
||||
path: 'mike/type/list',
|
||||
|
||||
@ -9,8 +9,14 @@ const routes: RouteRecordRaw[] = [
|
||||
},
|
||||
name: 'ResidentActivityManager',
|
||||
path: '/resident-activity/manager',
|
||||
redirect: '/resident-activity/manager/invite/config',
|
||||
redirect: '/resident-activity/manager/sign-in-reward/config',
|
||||
children: [
|
||||
{
|
||||
name: 'ResidentSignInRewardConfig',
|
||||
path: 'sign-in-reward/config',
|
||||
component: () => import('#/views/resident-activity/sign-in-reward-config.vue'),
|
||||
meta: { title: '签到奖励配置' },
|
||||
},
|
||||
{
|
||||
name: 'ResidentRegisterRewardConfig',
|
||||
path: 'register-reward/config',
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
import { computed, reactive, ref, watch } from 'vue';
|
||||
|
||||
import { saveOrUpdateSpecialId } from '#/api/legacy/app-system';
|
||||
import { getUserBaseInfoBySysOriginAccount } from '#/api/legacy/user';
|
||||
import AccountInput from '#/components/account-input.vue';
|
||||
|
||||
import {
|
||||
Button,
|
||||
@ -23,7 +25,7 @@ const props = defineProps<{
|
||||
|
||||
const emit = defineEmits<{
|
||||
close: [];
|
||||
success: [];
|
||||
success: [payload: { account: string; sysOrigin: string; userId: string }];
|
||||
}>();
|
||||
|
||||
const loading = ref(false);
|
||||
@ -37,6 +39,7 @@ const form = reactive<Record<string, any>>({
|
||||
remark: '',
|
||||
sysOrigin: '',
|
||||
userId: '',
|
||||
userSearch: '',
|
||||
});
|
||||
|
||||
const title = computed(() => (props.initialRecord?.id ? '修改' : '添加'));
|
||||
@ -50,6 +53,7 @@ function syncForm() {
|
||||
form.remark = record.remark ?? '';
|
||||
form.sysOrigin = record.sysOrigin ?? props.sysOrigin;
|
||||
form.userId = record.userId ?? '';
|
||||
form.userSearch = record.userId ?? '';
|
||||
}
|
||||
|
||||
watch(
|
||||
@ -64,7 +68,7 @@ watch(
|
||||
);
|
||||
|
||||
function validateForm() {
|
||||
if (!String(form.userId || '').trim()) {
|
||||
if (!String(form.userSearch || '').trim()) {
|
||||
message.warning('请输入用户ID');
|
||||
return false;
|
||||
}
|
||||
@ -79,21 +83,79 @@ function validateForm() {
|
||||
return true;
|
||||
}
|
||||
|
||||
function parseEncodedValue(value: unknown) {
|
||||
const raw = String(value ?? '');
|
||||
if (!raw) {
|
||||
return {
|
||||
accountType: '',
|
||||
content: '',
|
||||
sysOrigin: '',
|
||||
};
|
||||
}
|
||||
const match = raw.match(/^\*(.*)\*$/);
|
||||
if (!match) {
|
||||
return {
|
||||
accountType: '',
|
||||
content: raw,
|
||||
sysOrigin: '',
|
||||
};
|
||||
}
|
||||
const body = match[1] || '';
|
||||
return {
|
||||
accountType: body.match(/accountType:([^,]*)/)?.[1] || '',
|
||||
content: body.match(/content:(.*)$/)?.[1] || '',
|
||||
sysOrigin: body.match(/sysOrigin:([^,]*)/)?.[1] || '',
|
||||
};
|
||||
}
|
||||
|
||||
async function resolveUserId() {
|
||||
const parsed = parseEncodedValue(form.userSearch);
|
||||
const content = String(parsed.content || '').trim();
|
||||
if (!content) {
|
||||
message.warning('请输入用户ID');
|
||||
return '';
|
||||
}
|
||||
if (!parsed.accountType || parsed.accountType === 'LONG') {
|
||||
return content.replace(/[^\d]/g, '');
|
||||
}
|
||||
const sysOrigin = parsed.sysOrigin || form.sysOrigin || props.sysOrigin;
|
||||
if (!sysOrigin) {
|
||||
message.warning('请选择系统');
|
||||
return '';
|
||||
}
|
||||
const result = await getUserBaseInfoBySysOriginAccount(sysOrigin, content);
|
||||
const userId = result?.id || result?.userId;
|
||||
if (!userId) {
|
||||
message.warning('未找到用户');
|
||||
return '';
|
||||
}
|
||||
return String(userId);
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
if (!validateForm()) {
|
||||
return;
|
||||
}
|
||||
const resolvedUserId = await resolveUserId();
|
||||
if (!resolvedUserId) {
|
||||
return;
|
||||
}
|
||||
const payload = {
|
||||
account: String(form.account || '').trim(),
|
||||
sysOrigin: form.sysOrigin || props.sysOrigin,
|
||||
userId: resolvedUserId,
|
||||
};
|
||||
loading.value = true;
|
||||
try {
|
||||
await saveOrUpdateSpecialId({
|
||||
...form,
|
||||
account: String(form.account || '').trim(),
|
||||
account: payload.account,
|
||||
remark: String(form.remark || '').trim(),
|
||||
sysOrigin: form.sysOrigin || props.sysOrigin,
|
||||
userId: String(form.userId || '').trim(),
|
||||
sysOrigin: payload.sysOrigin,
|
||||
userId: payload.userId,
|
||||
});
|
||||
message.success('操作成功');
|
||||
emit('success');
|
||||
emit('success', payload);
|
||||
emit('close');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
@ -114,9 +176,10 @@ async function handleSubmit() {
|
||||
<div class="readonly-value">{{ sysOrigin }}</div>
|
||||
</FormItem>
|
||||
<FormItem label="用户">
|
||||
<Input
|
||||
v-model:value="form.userId"
|
||||
<AccountInput
|
||||
v-model:value="form.userSearch"
|
||||
:disabled="loading"
|
||||
:sys-origin="form.sysOrigin || props.sysOrigin"
|
||||
placeholder="请输入用户ID"
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
@ -7,7 +7,7 @@ import { formatDate } from '#/views/system/shared';
|
||||
import { Drawer, Empty, Spin } from 'antdv-next';
|
||||
|
||||
const props = defineProps<{
|
||||
account: number | string;
|
||||
userId: number | string;
|
||||
open: boolean;
|
||||
}>();
|
||||
|
||||
@ -19,14 +19,14 @@ const loading = ref(false);
|
||||
const list = ref<Array<Record<string, any>>>([]);
|
||||
|
||||
watch(
|
||||
() => ({ account: props.account, open: props.open }),
|
||||
async ({ account, open }) => {
|
||||
if (!open || !account) {
|
||||
() => ({ open: props.open, userId: props.userId }),
|
||||
async ({ open, userId }) => {
|
||||
if (!open || !userId) {
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
try {
|
||||
list.value = await listSpecialIdLatestLog(account as number | string);
|
||||
list.value = await listSpecialIdLatestLog(userId as number | string);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
@ -55,7 +55,7 @@ watch(
|
||||
<div class="time">{{ formatDate(item.createTime) }}</div>
|
||||
<div class="desc">{{ item.optUserNickname || '-' }},{{ item.eventDesc || '-' }}</div>
|
||||
<div class="sub">
|
||||
用户ID {{ item.userBaseInfo?.id || '-' }} / {{ item.userBaseInfo?.userNickname || '-' }}
|
||||
用户ID {{ item.userBaseInfo?.id || item.userId || '-' }} / {{ item.userBaseInfo?.userNickname || '-' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -46,6 +46,12 @@ import SpecialIdLogDrawer from './components/special-id-log-drawer.vue';
|
||||
|
||||
defineOptions({ name: 'AppSystemSpecialIdManager' });
|
||||
|
||||
type SpecialIdFormSuccessPayload = {
|
||||
account: string;
|
||||
sysOrigin: string;
|
||||
userId: string;
|
||||
};
|
||||
|
||||
const router = useRouter();
|
||||
const accessStore = useAccessStore();
|
||||
|
||||
@ -122,33 +128,44 @@ if (!showSettingTab.value && showLogTab.value) {
|
||||
activeTab.value = 'logs';
|
||||
}
|
||||
|
||||
function getEffectiveSpecialQuery() {
|
||||
const account = String(settingQuery.account || '').trim();
|
||||
const userId = String(settingQuery.userId || '').trim();
|
||||
function getEffectiveSpecialQuery(
|
||||
query: Partial<typeof settingQuery> = {},
|
||||
) {
|
||||
const account = String(query.account ?? settingQuery.account ?? '').trim();
|
||||
const userId = String(query.userId ?? settingQuery.userId ?? '').trim();
|
||||
return {
|
||||
...settingQuery,
|
||||
account: account.length === 0 && userId.length === 0 ? 'aaaaa' : account,
|
||||
...query,
|
||||
account,
|
||||
userId,
|
||||
};
|
||||
}
|
||||
|
||||
function getUserInfo(record: LegacySpecialIdFlowItem) {
|
||||
function getUserInfo(record: Record<string, any>) {
|
||||
return record.userBaseInfo || {};
|
||||
}
|
||||
|
||||
function getSpecialInfo(record: LegacySpecialIdFlowItem) {
|
||||
function getSpecialInfo(record: Record<string, any>) {
|
||||
return record.userSpecialId || {};
|
||||
}
|
||||
|
||||
function openUserDetails(record: LegacySpecialIdFlowItem) {
|
||||
const userId = getUserInfo(record).id;
|
||||
function getLogUserId(record: Record<string, any>) {
|
||||
return getUserInfo(record).id || record.userId || '';
|
||||
}
|
||||
|
||||
function openUserDetails(record: Record<string, any>) {
|
||||
const userId = getLogUserId(record);
|
||||
if (!userId) {
|
||||
return;
|
||||
}
|
||||
router.push(`/common/user/deatils/${userId}`);
|
||||
}
|
||||
|
||||
async function loadSettingData(reset = false, append = false) {
|
||||
async function loadSettingData(
|
||||
reset = false,
|
||||
append = false,
|
||||
query: Partial<typeof settingQuery> = {},
|
||||
) {
|
||||
if (reset) {
|
||||
settingQuery.lastId = '';
|
||||
list.value = [];
|
||||
@ -159,7 +176,7 @@ async function loadSettingData(reset = false, append = false) {
|
||||
loading.value = true;
|
||||
}
|
||||
try {
|
||||
const result = await specialIdFlow(getEffectiveSpecialQuery());
|
||||
const result = await specialIdFlow(getEffectiveSpecialQuery(query));
|
||||
const current = result || [];
|
||||
notData.value = current.length <= 0;
|
||||
if (!notData.value) {
|
||||
@ -197,6 +214,13 @@ function openCreate() {
|
||||
formOpen.value = true;
|
||||
}
|
||||
|
||||
function handleCreateSuccess(payload: SpecialIdFormSuccessPayload) {
|
||||
settingQuery.account = payload.account;
|
||||
settingQuery.sysOrigin = payload.sysOrigin;
|
||||
settingQuery.userId = payload.userId;
|
||||
loadSettingData(true, false, payload);
|
||||
}
|
||||
|
||||
function openLatestLog(record: LegacySpecialIdFlowItem) {
|
||||
selectedRow.value = record;
|
||||
latestLogOpen.value = true;
|
||||
@ -496,7 +520,7 @@ loadLogData(true);
|
||||
>
|
||||
<div class="user-copy">
|
||||
<span class="user-name">{{ record.userBaseInfo?.userNickname || '-' }}</span>
|
||||
<span class="user-sub">ID {{ record.userBaseInfo?.id || '-' }}</span>
|
||||
<span class="user-sub">ID {{ getLogUserId(record as any) || '-' }}</span>
|
||||
</div>
|
||||
</button>
|
||||
</template>
|
||||
@ -524,11 +548,11 @@ loadLogData(true);
|
||||
:open="formOpen"
|
||||
:sys-origin="settingQuery.sysOrigin"
|
||||
@close="formOpen = false"
|
||||
@success="loadSettingData(true, false)"
|
||||
@success="handleCreateSuccess"
|
||||
/>
|
||||
|
||||
<SpecialIdLogDrawer
|
||||
:account="getSpecialInfo(selectedRow || {}).userId || ''"
|
||||
:user-id="getSpecialInfo(selectedRow || {}).userId || ''"
|
||||
:open="latestLogOpen"
|
||||
@close="latestLogOpen = false"
|
||||
/>
|
||||
|
||||
470
apps/src/views/operate/baishun-game-manage.vue
Normal file
470
apps/src/views/operate/baishun-game-manage.vue
Normal file
@ -0,0 +1,470 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, reactive, ref, watch } from 'vue';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
|
||||
import {
|
||||
deleteBaishunGame,
|
||||
importBaishunCatalog,
|
||||
pageBaishunGames,
|
||||
saveBaishunGame,
|
||||
syncBaishunGames,
|
||||
} from '#/api/legacy/baishun-game';
|
||||
import { getAllowedSysOrigins } from '#/views/system/shared';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Form,
|
||||
FormItem,
|
||||
Image,
|
||||
Input,
|
||||
InputNumber,
|
||||
Modal,
|
||||
Pagination,
|
||||
Select,
|
||||
Space,
|
||||
Table,
|
||||
Tag,
|
||||
message,
|
||||
} from 'antdv-next';
|
||||
|
||||
import {
|
||||
GAME_CATEGORY_OPTIONS,
|
||||
GAME_CLIENT_ORIGIN_OPTIONS,
|
||||
} from './constants';
|
||||
|
||||
defineOptions({ name: 'OperateBaishunGameManage' });
|
||||
|
||||
function createForm(sysOrigin = '') {
|
||||
return {
|
||||
amounts: '',
|
||||
category: 'OTHER',
|
||||
clientOrigin: 'COMMON',
|
||||
cover: '',
|
||||
fullScreen: true as any,
|
||||
gameCode: '',
|
||||
gameId: '',
|
||||
gameMode: 3 as any,
|
||||
gsp: '',
|
||||
height: 0,
|
||||
id: '',
|
||||
launchMode: 'H5_REMOTE',
|
||||
name: '',
|
||||
orientation: undefined as any,
|
||||
packageUrl: '',
|
||||
packageVersion: '',
|
||||
previewUrl: '',
|
||||
regions: '',
|
||||
safeHeight: 0,
|
||||
showcase: false as any,
|
||||
sort: 0,
|
||||
sysOrigin,
|
||||
vendorGameId: undefined as any,
|
||||
width: 0,
|
||||
currencyIcon: '',
|
||||
};
|
||||
}
|
||||
|
||||
const accessStore = useAccessStore();
|
||||
const sysOriginOptions = computed(() => {
|
||||
const options = getAllowedSysOrigins(accessStore.accessCodes || []);
|
||||
return options.length > 0 ? options : getAllowedSysOrigins([]);
|
||||
});
|
||||
|
||||
const booleanOptions = [
|
||||
{ label: '是', value: true as any },
|
||||
{ label: '否', value: false as any },
|
||||
];
|
||||
const showcaseOptions = [
|
||||
{ label: '全部', value: '' as any },
|
||||
{ label: '上架', value: 'true' as any },
|
||||
{ label: '下架', value: 'false' as any },
|
||||
];
|
||||
const clientOriginOptions = GAME_CLIENT_ORIGIN_OPTIONS.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.value as any,
|
||||
}));
|
||||
const categoryOptions = GAME_CATEGORY_OPTIONS.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.value as any,
|
||||
}));
|
||||
const launchModeOptions = [
|
||||
{ label: 'H5_REMOTE', value: 'H5_REMOTE' },
|
||||
{ label: 'H5_ZIP_LOCAL', value: 'H5_ZIP_LOCAL' },
|
||||
];
|
||||
const gameModeOptions = [
|
||||
{ label: '1', value: 1 as any },
|
||||
{ label: '2 半屏', value: 2 as any },
|
||||
{ label: '3 默认', value: 3 as any },
|
||||
];
|
||||
const orientationOptions = [
|
||||
{ label: '0', value: 0 as any },
|
||||
{ label: '1', value: 1 as any },
|
||||
{ label: '2', value: 2 as any },
|
||||
];
|
||||
|
||||
const query = reactive<Record<string, any>>({
|
||||
cursor: 1,
|
||||
keyword: '',
|
||||
limit: 20,
|
||||
showcase: '',
|
||||
sysOrigin: '',
|
||||
});
|
||||
|
||||
const loading = ref(false);
|
||||
const saving = ref(false);
|
||||
const importing = ref(false);
|
||||
const syncing = ref(false);
|
||||
const total = ref(0);
|
||||
const modalOpen = ref(false);
|
||||
const list = ref<Array<Record<string, any>>>([]);
|
||||
const form = reactive<Record<string, any>>(createForm());
|
||||
|
||||
const columns = [
|
||||
{ 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: 'showcase', key: 'showcase', title: '状态', width: 100 },
|
||||
{ dataIndex: 'clientOrigin', key: 'clientOrigin', title: '客户端', width: 100 },
|
||||
{ dataIndex: 'gameMode', key: 'gameMode', title: 'Game Mode', width: 110 },
|
||||
{ dataIndex: 'launchMode', key: 'launchMode', title: '启动模式', width: 130 },
|
||||
{ dataIndex: 'safeHeight', key: 'safeHeight', title: '安全区', width: 110 },
|
||||
{ dataIndex: 'sort', key: 'sort', title: '排序', width: 90 },
|
||||
{ dataIndex: 'updateTime', key: 'updateTime', title: '更新时间', width: 180 },
|
||||
{ dataIndex: 'actions', key: 'actions', title: '操作', width: 160 },
|
||||
];
|
||||
|
||||
watch(
|
||||
sysOriginOptions,
|
||||
(options) => {
|
||||
if (!query.sysOrigin) {
|
||||
query.sysOrigin = String(options[0]?.value || '');
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
watch(
|
||||
() => query.sysOrigin,
|
||||
(value) => {
|
||||
if (value) {
|
||||
void loadData(true);
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
function resetForm() {
|
||||
Object.assign(form, createForm(query.sysOrigin));
|
||||
}
|
||||
|
||||
function openCreate() {
|
||||
resetForm();
|
||||
modalOpen.value = true;
|
||||
}
|
||||
|
||||
function openEdit(record: Record<string, any>) {
|
||||
Object.assign(form, createForm(query.sysOrigin), {
|
||||
...record,
|
||||
id: String(record.id || ''),
|
||||
vendorGameId: Number(record.vendorGameId || 0) || undefined,
|
||||
});
|
||||
modalOpen.value = true;
|
||||
}
|
||||
|
||||
async function loadData(reset = false) {
|
||||
if (!query.sysOrigin) {
|
||||
return;
|
||||
}
|
||||
if (reset) {
|
||||
query.cursor = 1;
|
||||
}
|
||||
loading.value = true;
|
||||
try {
|
||||
const result = await pageBaishunGames({ ...query });
|
||||
list.value = Array.isArray(result.records) ? result.records : [];
|
||||
total.value = Number(result.total || 0);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
void loadData(true);
|
||||
}
|
||||
|
||||
function handlePageChange(page: number, pageSize: number) {
|
||||
query.cursor = page;
|
||||
query.limit = pageSize;
|
||||
void loadData();
|
||||
}
|
||||
|
||||
async function submitForm() {
|
||||
if (!form.sysOrigin || !form.name || !form.gameId || !form.vendorGameId) {
|
||||
message.warning('请补全系统、名称、内部ID和百顺ID');
|
||||
return;
|
||||
}
|
||||
saving.value = true;
|
||||
try {
|
||||
await saveBaishunGame({
|
||||
...form,
|
||||
id: Number(form.id || 0),
|
||||
vendorGameId: Number(form.vendorGameId || 0),
|
||||
});
|
||||
message.success('保存成功');
|
||||
modalOpen.value = false;
|
||||
await loadData(true);
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleDelete(record: Record<string, any>) {
|
||||
Modal.confirm({
|
||||
title: '确认删除这个游戏配置吗?',
|
||||
async onOk() {
|
||||
await deleteBaishunGame(record.id, record.sysOrigin);
|
||||
message.success('删除成功');
|
||||
await loadData(true);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function handleImportCatalog() {
|
||||
if (!query.sysOrigin) {
|
||||
return;
|
||||
}
|
||||
importing.value = true;
|
||||
try {
|
||||
const result = await importBaishunCatalog({ sysOrigin: query.sysOrigin });
|
||||
message.success(`导入完成:新增 ${Number(result.imported || 0)},已存在 ${Number(result.existing || 0)}`);
|
||||
await loadData(true);
|
||||
} finally {
|
||||
importing.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSync() {
|
||||
if (!query.sysOrigin) {
|
||||
return;
|
||||
}
|
||||
syncing.value = true;
|
||||
try {
|
||||
const result = await syncBaishunGames({ sysOrigin: query.sysOrigin });
|
||||
message.success(
|
||||
`同步完成:目录新增 ${Number(result.inserted || 0)},更新 ${Number(result.updated || 0)},导入 ${Number(result.imported || 0)}`,
|
||||
);
|
||||
await loadData(true);
|
||||
} finally {
|
||||
syncing.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page title="百顺游戏管理">
|
||||
<Card>
|
||||
<div class="toolbar">
|
||||
<SysOriginSelect
|
||||
v-model:value="query.sysOrigin"
|
||||
style="width: 140px"
|
||||
:options="sysOriginOptions"
|
||||
/>
|
||||
<Select
|
||||
v-model:value="query.showcase"
|
||||
style="width: 120px"
|
||||
:options="showcaseOptions"
|
||||
@change="handleSearch"
|
||||
/>
|
||||
<Input
|
||||
v-model:value="query.keyword"
|
||||
placeholder="搜索名称 / 内部ID / 百顺ID"
|
||||
style="width: 240px"
|
||||
@pressEnter="handleSearch"
|
||||
/>
|
||||
<Button type="primary" :loading="loading" @click="handleSearch">
|
||||
搜索
|
||||
</Button>
|
||||
<Button :loading="importing" @click="handleImportCatalog">
|
||||
导入本地目录
|
||||
</Button>
|
||||
<Button :loading="syncing" @click="handleSync">
|
||||
同步百顺目录
|
||||
</Button>
|
||||
<Button type="primary" @click="openCreate">
|
||||
新增
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Table
|
||||
:columns="columns"
|
||||
:data-source="list"
|
||||
:loading="loading"
|
||||
:pagination="false"
|
||||
row-key="id"
|
||||
:scroll="{ x: 1600 }"
|
||||
>
|
||||
<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 === '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="total > 0" class="pager">
|
||||
<Pagination
|
||||
:current="query.cursor"
|
||||
:page-size="query.limit"
|
||||
:total="total"
|
||||
show-size-changer
|
||||
@change="handlePageChange"
|
||||
@showSizeChange="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Modal
|
||||
:confirm-loading="saving"
|
||||
:open="modalOpen"
|
||||
destroy-on-close
|
||||
:title="form.id ? '编辑百顺游戏' : '新增百顺游戏'"
|
||||
width="860px"
|
||||
@cancel="modalOpen = false"
|
||||
@ok="submitForm"
|
||||
>
|
||||
<Form layout="vertical">
|
||||
<div class="form-grid">
|
||||
<FormItem label="系统">
|
||||
<SysOriginSelect v-model:value="form.sysOrigin" :options="sysOriginOptions" />
|
||||
</FormItem>
|
||||
<FormItem label="名称">
|
||||
<Input v-model:value="form.name" placeholder="请输入游戏名称" />
|
||||
</FormItem>
|
||||
<FormItem label="内部ID">
|
||||
<Input v-model:value="form.gameId" placeholder="例如 bs_1100" />
|
||||
</FormItem>
|
||||
<FormItem label="百顺ID">
|
||||
<InputNumber v-model:value="form.vendorGameId" :min="1" style="width: 100%" />
|
||||
</FormItem>
|
||||
<FormItem label="编号">
|
||||
<Input v-model:value="form.gameCode" placeholder="默认取百顺ID" />
|
||||
</FormItem>
|
||||
<FormItem label="分类">
|
||||
<Select v-model:value="form.category" :options="categoryOptions" />
|
||||
</FormItem>
|
||||
<FormItem label="客户端">
|
||||
<Select v-model:value="form.clientOrigin" :options="clientOriginOptions" />
|
||||
</FormItem>
|
||||
<FormItem label="状态">
|
||||
<Select v-model:value="form.showcase" :options="booleanOptions" />
|
||||
</FormItem>
|
||||
<FormItem label="是否全屏">
|
||||
<Select v-model:value="form.fullScreen" :options="booleanOptions" />
|
||||
</FormItem>
|
||||
<FormItem label="Game Mode">
|
||||
<Select v-model:value="form.gameMode" :options="gameModeOptions" />
|
||||
</FormItem>
|
||||
<FormItem label="启动模式">
|
||||
<Select v-model:value="form.launchMode" :options="launchModeOptions" />
|
||||
</FormItem>
|
||||
<FormItem label="排序">
|
||||
<InputNumber v-model:value="form.sort" :min="0" style="width: 100%" />
|
||||
</FormItem>
|
||||
<FormItem label="安全区高度">
|
||||
<InputNumber v-model:value="form.safeHeight" :min="0" style="width: 100%" />
|
||||
</FormItem>
|
||||
<FormItem label="Orientation">
|
||||
<Select
|
||||
v-model:value="form.orientation"
|
||||
:options="orientationOptions"
|
||||
allow-clear
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="宽">
|
||||
<InputNumber v-model:value="form.width" :min="0" style="width: 100%" />
|
||||
</FormItem>
|
||||
<FormItem label="高">
|
||||
<InputNumber v-model:value="form.height" :min="0" style="width: 100%" />
|
||||
</FormItem>
|
||||
<FormItem label="GSP">
|
||||
<Input v-model:value="form.gsp" placeholder="默认取目录或系统配置" />
|
||||
</FormItem>
|
||||
<FormItem label="金额">
|
||||
<Input v-model:value="form.amounts" placeholder="可选" />
|
||||
</FormItem>
|
||||
<FormItem label="区域">
|
||||
<Input v-model:value="form.regions" placeholder="多个区域用逗号分隔" />
|
||||
</FormItem>
|
||||
<FormItem label="货币图标">
|
||||
<Input v-model:value="form.currencyIcon" placeholder="可选" />
|
||||
</FormItem>
|
||||
</div>
|
||||
<FormItem label="封面">
|
||||
<Input v-model:value="form.cover" placeholder="请输入封面 URL" />
|
||||
</FormItem>
|
||||
<FormItem label="预览地址">
|
||||
<Input v-model:value="form.previewUrl" placeholder="请输入预览地址" />
|
||||
</FormItem>
|
||||
<FormItem label="包地址">
|
||||
<Input v-model:value="form.packageUrl" placeholder="请输入包地址" />
|
||||
</FormItem>
|
||||
<FormItem label="包版本">
|
||||
<Input v-model:value="form.packageVersion" placeholder="请输入包版本" />
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Modal>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.toolbar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.pager {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.cover {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
object-fit: cover;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.form-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 0 16px;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.form-grid {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -302,6 +302,10 @@ function openFruitConfig() {
|
||||
router.push('/operate/manager/game/fruit/task-config');
|
||||
}
|
||||
|
||||
function openBaishunManager() {
|
||||
router.push('/operate/manager/baishun-game-manage');
|
||||
}
|
||||
|
||||
void loadData(true);
|
||||
</script>
|
||||
|
||||
@ -329,6 +333,9 @@ void loadData(true);
|
||||
<Button type="primary" @click="openCreate">
|
||||
新增
|
||||
</Button>
|
||||
<Button @click="openBaishunManager">
|
||||
百顺游戏管理
|
||||
</Button>
|
||||
<Button @click="openFruitConfig">
|
||||
摩天轮任务配置
|
||||
</Button>
|
||||
|
||||
@ -48,6 +48,7 @@ const query = reactive<Record<string, any>>({
|
||||
const form = reactive<Record<string, any>>({
|
||||
aliasName: '',
|
||||
countryName: '',
|
||||
countryCodeAliasesInput: '',
|
||||
id: '',
|
||||
nationalFlag: '',
|
||||
open: false,
|
||||
@ -59,6 +60,7 @@ const form = reactive<Record<string, any>>({
|
||||
const columns: any[] = [
|
||||
{ dataIndex: 'countryName', key: 'countryName', title: '国家名称', width: 200 },
|
||||
{ dataIndex: 'aliasName', key: 'aliasName', title: '别名', width: 160 },
|
||||
{ dataIndex: 'countryCodeAliases', key: 'countryCodeAliases', title: '国家码集合', width: 220 },
|
||||
{ dataIndex: 'nationalFlag', key: 'nationalFlag', title: '国旗', width: 120 },
|
||||
{ dataIndex: 'open', key: 'open', title: '开放状态', width: 120 },
|
||||
{ dataIndex: 'phonePrefix', key: 'phonePrefix', title: '手机号码前缀', width: 140 },
|
||||
@ -94,6 +96,9 @@ function handlePageChange(page: number, pageSize: number) {
|
||||
function openEdit(record: Record<string, any>) {
|
||||
form.aliasName = record.aliasName || '';
|
||||
form.countryName = record.countryName || '';
|
||||
form.countryCodeAliasesInput = Array.isArray(record.countryCodeAliases)
|
||||
? record.countryCodeAliases.join(',')
|
||||
: '';
|
||||
form.id = record.id || '';
|
||||
form.nationalFlag = record.nationalFlag || '';
|
||||
form.open = record.open ?? false;
|
||||
@ -144,7 +149,15 @@ async function saveForm() {
|
||||
}
|
||||
submitLoading.value = true;
|
||||
try {
|
||||
await updateSysCountryCode({ ...form });
|
||||
const payload: Record<string, any> = {
|
||||
...form,
|
||||
countryCodeAliases: String(form.countryCodeAliasesInput || '')
|
||||
.split(',')
|
||||
.map((item) => item.trim().toUpperCase())
|
||||
.filter(Boolean),
|
||||
};
|
||||
delete payload.countryCodeAliasesInput;
|
||||
await updateSysCountryCode(payload);
|
||||
message.success('保存成功');
|
||||
formOpen.value = false;
|
||||
await loadData();
|
||||
@ -198,6 +211,9 @@ loadData(true);
|
||||
<template v-else-if="column.key === 'nationalFlag'">
|
||||
<Image :preview="false" :src="record.nationalFlag" class="flag" />
|
||||
</template>
|
||||
<template v-else-if="column.key === 'countryCodeAliases'">
|
||||
{{ Array.isArray(record.countryCodeAliases) ? record.countryCodeAliases.join(', ') : '-' }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'open'">
|
||||
{{ record.open ? '已开放' : '未开放' }}
|
||||
</template>
|
||||
@ -282,6 +298,9 @@ loadData(true);
|
||||
<FormItem label="别名">
|
||||
<Input v-model:value="form.aliasName" />
|
||||
</FormItem>
|
||||
<FormItem label="国家码集合">
|
||||
<Input v-model:value="form.countryCodeAliasesInput" placeholder="例如:SA,KSA" />
|
||||
</FormItem>
|
||||
<FormItem label="手机号前缀">
|
||||
<Input v-model:value="form.phonePrefix" />
|
||||
</FormItem>
|
||||
|
||||
574
apps/src/views/resident-activity/sign-in-reward-config.vue
Normal file
574
apps/src/views/resident-activity/sign-in-reward-config.vue
Normal file
@ -0,0 +1,574 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, reactive, ref, watch } from 'vue';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
|
||||
import {
|
||||
getResidentSignInRewardConfig,
|
||||
pageResidentSignInRewardRecords,
|
||||
saveResidentSignInRewardConfig,
|
||||
} from '#/api/legacy/resident-activity';
|
||||
import ActivityResourceGroupSelectDrawer from '#/views/props/components/activity-resource-group-select-drawer.vue';
|
||||
import UserProfileLink from '#/views/operate/components/user-profile-link.vue';
|
||||
import { getAllowedSysOrigins } from '#/views/system/shared';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
DatePicker,
|
||||
Input,
|
||||
Modal,
|
||||
Pagination,
|
||||
Select,
|
||||
Space,
|
||||
Spin,
|
||||
Switch,
|
||||
Table,
|
||||
Tag,
|
||||
message,
|
||||
} from 'antdv-next';
|
||||
|
||||
defineOptions({ name: 'ResidentSignInRewardConfigPage' });
|
||||
|
||||
function createDay(dayIndex: number) {
|
||||
return {
|
||||
dayIndex,
|
||||
rewardGroupId: null as null | number | string,
|
||||
rewardGroupName: '',
|
||||
};
|
||||
}
|
||||
|
||||
function createEmptyForm(sysOrigin = '') {
|
||||
return {
|
||||
configured: false,
|
||||
cycleDays: 7,
|
||||
enabled: false,
|
||||
id: 0,
|
||||
sysOrigin,
|
||||
timezone: 'Asia/Riyadh',
|
||||
updateTime: '',
|
||||
days: Array.from({ length: 7 }, (_, index) => createDay(index + 1)),
|
||||
};
|
||||
}
|
||||
|
||||
const accessStore = useAccessStore();
|
||||
const timezoneOptions = [
|
||||
{ label: '沙特阿拉伯 (Asia/Riyadh)', value: 'Asia/Riyadh' },
|
||||
{ label: '阿联酋 (Asia/Dubai)', value: 'Asia/Dubai' },
|
||||
{ label: 'UTC', value: 'UTC' },
|
||||
{ label: '中国 (Asia/Shanghai)', value: 'Asia/Shanghai' },
|
||||
{ label: '巴西 (America/Sao_Paulo)', value: 'America/Sao_Paulo' },
|
||||
];
|
||||
|
||||
const sysOriginOptions = computed(() => {
|
||||
const options = getAllowedSysOrigins(accessStore.accessCodes || []);
|
||||
return options.length > 0 ? options : getAllowedSysOrigins([]);
|
||||
});
|
||||
|
||||
const loading = ref(false);
|
||||
const saving = ref(false);
|
||||
const recordLoading = ref(false);
|
||||
const recordTotal = ref(0);
|
||||
const groupPickerOpen = ref(false);
|
||||
const pickerDayIndex = ref<number>(1);
|
||||
const recordList = ref<Array<Record<string, any>>>([]);
|
||||
|
||||
const dayColumns = [
|
||||
{ dataIndex: 'dayIndex', key: 'dayIndex', title: '签到日', width: 120 },
|
||||
{ dataIndex: 'rewardGroup', key: 'rewardGroup', title: '奖励组', width: 420 },
|
||||
];
|
||||
|
||||
const recordColumns = [
|
||||
{ dataIndex: 'user', key: 'user', title: '签到用户', width: 280 },
|
||||
{ dataIndex: 'claimDate', key: 'claimDate', title: '签到日期', width: 120 },
|
||||
{ dataIndex: 'dayIndex', key: 'dayIndex', title: '循环日', width: 100 },
|
||||
{ dataIndex: 'streakCount', key: 'streakCount', title: '连续天数', width: 100 },
|
||||
{ dataIndex: 'rewardPayload', key: 'rewardPayload', title: '奖励内容', width: 220 },
|
||||
{ dataIndex: 'status', key: 'status', title: '状态', width: 120 },
|
||||
{ dataIndex: 'createTime', key: 'createTime', title: '签到时间', width: 180 },
|
||||
{ dataIndex: 'errorMessage', key: 'errorMessage', title: '备注 / 失败原因', width: 260 },
|
||||
];
|
||||
|
||||
const form = reactive<Record<string, any>>(createEmptyForm());
|
||||
const recordQuery = reactive<Record<string, any>>({
|
||||
claimDate: '',
|
||||
cursor: 1,
|
||||
limit: 20,
|
||||
status: '',
|
||||
userId: '',
|
||||
});
|
||||
const recordSummary = reactive<Record<string, any>>({
|
||||
date: '',
|
||||
signCount: 0,
|
||||
successCount: 0,
|
||||
timezone: '',
|
||||
});
|
||||
|
||||
const statusMeta = computed(() => {
|
||||
if (!form.configured) {
|
||||
return {
|
||||
color: 'processing',
|
||||
description: '当前未配置,app 调签到奖励接口时会返回不可用。',
|
||||
text: '未配置',
|
||||
};
|
||||
}
|
||||
if (form.enabled) {
|
||||
return {
|
||||
color: 'success',
|
||||
description: '当前已启用,用户每天签到一次,按 7 天配置循环领奖。',
|
||||
text: '已启用',
|
||||
};
|
||||
}
|
||||
return {
|
||||
color: 'default',
|
||||
description: '当前已关闭,app 可读配置但不可签到领奖。',
|
||||
text: '已关闭',
|
||||
};
|
||||
});
|
||||
|
||||
function normalizeDays(days: Array<Record<string, any>> | undefined) {
|
||||
const dayMap = new Map<number, Record<string, any>>();
|
||||
for (const item of days || []) {
|
||||
const dayIndex = Number(item.dayIndex || 0);
|
||||
if (dayIndex >= 1 && dayIndex <= 7) {
|
||||
dayMap.set(dayIndex, {
|
||||
dayIndex,
|
||||
rewardGroupId: item.rewardGroupId ?? null,
|
||||
rewardGroupName: String(item.rewardGroupName || ''),
|
||||
});
|
||||
}
|
||||
}
|
||||
return Array.from({ length: 7 }, (_, index) => {
|
||||
const dayIndex = index + 1;
|
||||
return dayMap.get(dayIndex) || createDay(dayIndex);
|
||||
});
|
||||
}
|
||||
|
||||
function applyConfig(result: Record<string, any> | null | undefined) {
|
||||
const value = result ?? {};
|
||||
const next = createEmptyForm(
|
||||
String(value.sysOrigin || form.sysOrigin || sysOriginOptions.value[0]?.value || ''),
|
||||
);
|
||||
Object.assign(form, next, {
|
||||
configured: Boolean(value.configured),
|
||||
cycleDays: Number(value.cycleDays || 7),
|
||||
enabled: Boolean(value.enabled),
|
||||
id: Number(value.id || 0),
|
||||
sysOrigin: String(value.sysOrigin || next.sysOrigin || ''),
|
||||
timezone: String(value.timezone || 'Asia/Riyadh'),
|
||||
updateTime: String(value.updateTime || ''),
|
||||
});
|
||||
form.days = normalizeDays(value.days);
|
||||
}
|
||||
|
||||
function syncRecordSummary(result: Record<string, any> | null | undefined) {
|
||||
const value = result ?? {};
|
||||
recordSummary.date = String(value.date || recordSummary.date || '');
|
||||
recordSummary.signCount = Number(value.signCount || 0);
|
||||
recordSummary.successCount = Number(value.successCount || 0);
|
||||
recordSummary.timezone = String(value.timezone || recordSummary.timezone || '');
|
||||
}
|
||||
|
||||
async function loadConfig() {
|
||||
if (!form.sysOrigin) {
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
try {
|
||||
const result = await getResidentSignInRewardConfig(form.sysOrigin);
|
||||
applyConfig(result);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadRecords(reset = false) {
|
||||
if (!form.sysOrigin) {
|
||||
recordList.value = [];
|
||||
recordTotal.value = 0;
|
||||
return;
|
||||
}
|
||||
if (reset) {
|
||||
recordQuery.cursor = 1;
|
||||
}
|
||||
recordLoading.value = true;
|
||||
try {
|
||||
const result = await pageResidentSignInRewardRecords({
|
||||
...recordQuery,
|
||||
sysOrigin: form.sysOrigin,
|
||||
});
|
||||
recordList.value = Array.isArray(result.records) ? result.records : [];
|
||||
recordTotal.value = Number(result.total || 0);
|
||||
syncRecordSummary(result);
|
||||
if (!recordQuery.claimDate && result.date) {
|
||||
recordQuery.claimDate = String(result.date);
|
||||
}
|
||||
} finally {
|
||||
recordLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadData() {
|
||||
await Promise.all([loadConfig(), loadRecords(true)]);
|
||||
}
|
||||
|
||||
watch(
|
||||
sysOriginOptions,
|
||||
(options) => {
|
||||
if (!form.sysOrigin) {
|
||||
form.sysOrigin = String(options[0]?.value || '');
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
watch(
|
||||
() => form.sysOrigin,
|
||||
(value) => {
|
||||
if (value) {
|
||||
void loadData();
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
function openRewardPicker(dayIndex: number) {
|
||||
pickerDayIndex.value = dayIndex;
|
||||
groupPickerOpen.value = true;
|
||||
}
|
||||
|
||||
function clearRewardGroup(dayIndex: number) {
|
||||
const target = form.days.find((item: Record<string, any>) => item.dayIndex === dayIndex);
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
target.rewardGroupId = null;
|
||||
target.rewardGroupName = '';
|
||||
}
|
||||
|
||||
function handleRewardGroupSelect(row: Record<string, any>) {
|
||||
const target = form.days.find(
|
||||
(item: Record<string, any>) => item.dayIndex === pickerDayIndex.value,
|
||||
);
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
target.rewardGroupId = row.id;
|
||||
target.rewardGroupName = row.name;
|
||||
groupPickerOpen.value = false;
|
||||
}
|
||||
|
||||
async function submitForm() {
|
||||
saving.value = true;
|
||||
try {
|
||||
await saveResidentSignInRewardConfig({
|
||||
enabled: Boolean(form.enabled),
|
||||
sysOrigin: form.sysOrigin,
|
||||
timezone: String(form.timezone || 'Asia/Riyadh').trim(),
|
||||
days: (form.days || []).map((item: Record<string, any>) => ({
|
||||
dayIndex: Number(item.dayIndex || 0),
|
||||
rewardGroupId: item.rewardGroupId ?? null,
|
||||
rewardGroupName: String(item.rewardGroupName || ''),
|
||||
})),
|
||||
});
|
||||
message.success('保存成功');
|
||||
await loadConfig();
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleSave() {
|
||||
Modal.confirm({
|
||||
async onOk() {
|
||||
await submitForm();
|
||||
},
|
||||
title: '确认保存当前系统的签到奖励配置?',
|
||||
content: '保存后 app 会立即读取新的 7 天循环签到奖励配置。',
|
||||
});
|
||||
}
|
||||
|
||||
function recordStatusColor(status: string) {
|
||||
switch (String(status || '').toUpperCase()) {
|
||||
case 'SUCCESS':
|
||||
return 'success';
|
||||
case 'FAILED':
|
||||
return 'error';
|
||||
default:
|
||||
return 'processing';
|
||||
}
|
||||
}
|
||||
|
||||
function handleRecordSearch() {
|
||||
void loadRecords(true);
|
||||
}
|
||||
|
||||
function handleRecordPageChange(page: number, pageSize: number) {
|
||||
recordQuery.cursor = page;
|
||||
recordQuery.limit = pageSize;
|
||||
void loadRecords();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<Spin :spinning="loading">
|
||||
<Card :bordered="false" title="签到奖励配置">
|
||||
<Space class="toolbar" wrap>
|
||||
<Select
|
||||
v-model:value="form.sysOrigin"
|
||||
:options="sysOriginOptions"
|
||||
option-filter-prop="label"
|
||||
option-label-prop="label"
|
||||
placeholder="请选择系统"
|
||||
show-search
|
||||
style="width: 180px"
|
||||
/>
|
||||
<Tag :color="statusMeta.color">
|
||||
{{ statusMeta.text }}
|
||||
</Tag>
|
||||
<span class="status-desc">{{ statusMeta.description }}</span>
|
||||
</Space>
|
||||
|
||||
<div class="section">
|
||||
<div class="field-label">启用签到奖励</div>
|
||||
<Switch v-model:checked="form.enabled" />
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="field-label">签到时区</div>
|
||||
<Select
|
||||
v-model:value="form.timezone"
|
||||
:disabled="saving"
|
||||
:options="timezoneOptions"
|
||||
option-filter-prop="label"
|
||||
option-label-prop="label"
|
||||
placeholder="请选择时区"
|
||||
show-search
|
||||
style="width: 260px"
|
||||
/>
|
||||
<span class="field-desc">默认沙特阿拉伯时区,按所选时区切自然日。</span>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="field-label">循环规则</div>
|
||||
<span class="field-desc">固定 7 天循环,用户连续签到满 7 天后从第 1 天奖励重新开始。</span>
|
||||
</div>
|
||||
|
||||
<Table
|
||||
:columns="dayColumns"
|
||||
:data-source="form.days"
|
||||
:pagination="false"
|
||||
row-key="dayIndex"
|
||||
:scroll="{ x: 760 }"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'dayIndex'">
|
||||
第 {{ record.dayIndex }} 天
|
||||
</template>
|
||||
<template v-else-if="column.key === 'rewardGroup'">
|
||||
<Space wrap>
|
||||
<Button :disabled="!form.enabled" @click="openRewardPicker(record.dayIndex)">
|
||||
{{ record.rewardGroupId ? '更换奖励组' : '选择奖励组' }}
|
||||
</Button>
|
||||
<Button
|
||||
v-if="record.rewardGroupId"
|
||||
:disabled="!form.enabled"
|
||||
danger
|
||||
type="link"
|
||||
@click="clearRewardGroup(record.dayIndex)"
|
||||
>
|
||||
清空
|
||||
</Button>
|
||||
<span v-if="record.rewardGroupId" class="reward-group">
|
||||
{{ record.rewardGroupName || '未命名奖励组' }} (ID: {{ record.rewardGroupId }})
|
||||
</span>
|
||||
<span v-else class="reward-group empty">未选择奖励组</span>
|
||||
</Space>
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
|
||||
<div class="tips">
|
||||
<div>启用后 7 天都必须配置奖励组。</div>
|
||||
<div>签到状态按用户连续签到计算;中断一天后下次从第 1 天开始。</div>
|
||||
<div>奖励组明细由 app 侧接口实时读取,后台这里保存的是奖励组引用。</div>
|
||||
</div>
|
||||
|
||||
<Space class="actions" wrap>
|
||||
<Button :loading="saving" type="primary" @click="handleSave">
|
||||
保存配置
|
||||
</Button>
|
||||
<span v-if="form.updateTime" class="field-desc">最后更新时间:{{ form.updateTime }}</span>
|
||||
</Space>
|
||||
</Card>
|
||||
|
||||
<Card :bordered="false" class="record-card" title="签到记录">
|
||||
<div class="toolbar">
|
||||
<Space wrap>
|
||||
<DatePicker
|
||||
v-model:value="recordQuery.claimDate"
|
||||
placeholder="签到日期"
|
||||
style="width: 160px"
|
||||
value-format="YYYY-MM-DD"
|
||||
/>
|
||||
<Input
|
||||
v-model:value="recordQuery.userId"
|
||||
allow-clear
|
||||
placeholder="用户ID"
|
||||
style="width: 160px"
|
||||
/>
|
||||
<Select
|
||||
v-model:value="recordQuery.status"
|
||||
allow-clear
|
||||
:options="[
|
||||
{ label: '成功', value: 'SUCCESS' },
|
||||
{ label: '失败', value: 'FAILED' },
|
||||
{ label: '处理中', value: 'PENDING' },
|
||||
]"
|
||||
placeholder="状态"
|
||||
style="width: 140px"
|
||||
/>
|
||||
<Button :loading="recordLoading" type="primary" @click="handleRecordSearch">
|
||||
查询
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
<div class="summary">
|
||||
<Tag color="processing">
|
||||
统计日期:{{ recordSummary.date || '-' }}
|
||||
</Tag>
|
||||
<Tag color="purple">
|
||||
时区:{{ recordSummary.timezone || '-' }}
|
||||
</Tag>
|
||||
<Tag color="orange">
|
||||
当日签到数:{{ recordSummary.signCount }}
|
||||
</Tag>
|
||||
<Tag color="success">
|
||||
发奖成功数:{{ recordSummary.successCount }}
|
||||
</Tag>
|
||||
</div>
|
||||
|
||||
<Table
|
||||
:columns="recordColumns"
|
||||
:data-source="recordList"
|
||||
:loading="recordLoading"
|
||||
:pagination="false"
|
||||
row-key="id"
|
||||
:scroll="{ x: 1400 }"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'user'">
|
||||
<UserProfileLink
|
||||
:profile="{
|
||||
account: record.account,
|
||||
countryCode: record.countryCode,
|
||||
countryName: record.countryName,
|
||||
id: record.userId,
|
||||
userAvatar: record.userAvatar,
|
||||
userNickname: record.userNickname,
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'dayIndex'">
|
||||
第 {{ record.dayIndex || 0 }} 天
|
||||
</template>
|
||||
<template v-else-if="column.key === 'rewardPayload'">
|
||||
<div>奖励组:{{ record.rewardGroupName || record.rewardGroupId || '-' }}</div>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'status'">
|
||||
<Tag :color="recordStatusColor(record.status)">
|
||||
{{ record.status || '-' }}
|
||||
</Tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'errorMessage'">
|
||||
{{ record.errorMessage || '-' }}
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
|
||||
<div v-if="recordTotal > 0" class="pager">
|
||||
<Pagination
|
||||
:current="recordQuery.cursor"
|
||||
:page-size="recordQuery.limit"
|
||||
:total="recordTotal"
|
||||
show-size-changer
|
||||
@change="handleRecordPageChange"
|
||||
@showSizeChange="handleRecordPageChange"
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<ActivityResourceGroupSelectDrawer
|
||||
:open="groupPickerOpen"
|
||||
:sys-origin="form.sysOrigin"
|
||||
@close="groupPickerOpen = false"
|
||||
@select="handleRewardGroupSelect"
|
||||
/>
|
||||
</Spin>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.toolbar {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.status-desc {
|
||||
color: rgb(100 116 139);
|
||||
}
|
||||
|
||||
.section {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.field-label {
|
||||
color: rgb(15 23 42);
|
||||
flex: 0 0 120px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.field-desc {
|
||||
color: rgb(100 116 139);
|
||||
}
|
||||
|
||||
.reward-group {
|
||||
color: rgb(15 23 42);
|
||||
}
|
||||
|
||||
.reward-group.empty {
|
||||
color: rgb(148 163 184);
|
||||
}
|
||||
|
||||
.tips {
|
||||
background: rgb(248 250 252);
|
||||
border-radius: 8px;
|
||||
color: rgb(71 85 105);
|
||||
line-height: 1.8;
|
||||
margin-top: 16px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.record-card {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.summary {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.pager {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 16px;
|
||||
}
|
||||
</style>
|
||||
375
scripts/sql/sync_admin_resident_activity_and_baishun_menu.sql
Normal file
375
scripts/sql/sync_admin_resident_activity_and_baishun_menu.sql
Normal file
@ -0,0 +1,375 @@
|
||||
INSERT INTO sys_menu (
|
||||
id,
|
||||
parent_id,
|
||||
menu_name,
|
||||
path,
|
||||
menu_type,
|
||||
icon,
|
||||
create_user,
|
||||
update_user,
|
||||
sort,
|
||||
status,
|
||||
router,
|
||||
alias
|
||||
)
|
||||
SELECT
|
||||
1700000251,
|
||||
0,
|
||||
'常驻活动',
|
||||
'/resident-activity/manager',
|
||||
1,
|
||||
'example',
|
||||
'0',
|
||||
'0',
|
||||
195,
|
||||
0,
|
||||
NULL,
|
||||
'ResidentActivityManager'
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM sys_menu WHERE alias = 'ResidentActivityManager'
|
||||
);
|
||||
|
||||
UPDATE sys_menu
|
||||
SET
|
||||
parent_id = 0,
|
||||
menu_name = '常驻活动',
|
||||
path = '/resident-activity/manager',
|
||||
menu_type = 1,
|
||||
icon = 'example',
|
||||
create_user = '0',
|
||||
update_user = '0',
|
||||
sort = 195,
|
||||
status = 0,
|
||||
router = NULL
|
||||
WHERE alias = 'ResidentActivityManager';
|
||||
|
||||
INSERT INTO sys_menu (
|
||||
id,
|
||||
parent_id,
|
||||
menu_name,
|
||||
path,
|
||||
menu_type,
|
||||
icon,
|
||||
create_user,
|
||||
update_user,
|
||||
sort,
|
||||
status,
|
||||
router,
|
||||
alias
|
||||
)
|
||||
SELECT
|
||||
1700000252,
|
||||
(SELECT id FROM sys_menu WHERE alias = 'ResidentActivityManager' LIMIT 1),
|
||||
'签到奖励配置',
|
||||
'resident-activity/sign-in-reward-config/index',
|
||||
2,
|
||||
'form',
|
||||
'0',
|
||||
'0',
|
||||
1,
|
||||
0,
|
||||
'sign-in-reward/config',
|
||||
'ResidentSignInRewardConfig'
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM sys_menu WHERE alias = 'ResidentSignInRewardConfig'
|
||||
);
|
||||
|
||||
UPDATE sys_menu
|
||||
SET
|
||||
parent_id = (
|
||||
SELECT resident_menu.id
|
||||
FROM (
|
||||
SELECT id
|
||||
FROM sys_menu
|
||||
WHERE alias = 'ResidentActivityManager'
|
||||
LIMIT 1
|
||||
) AS resident_menu
|
||||
),
|
||||
menu_name = '签到奖励配置',
|
||||
path = 'resident-activity/sign-in-reward-config/index',
|
||||
menu_type = 2,
|
||||
icon = 'form',
|
||||
create_user = '0',
|
||||
update_user = '0',
|
||||
sort = 1,
|
||||
status = 0,
|
||||
router = 'sign-in-reward/config'
|
||||
WHERE alias = 'ResidentSignInRewardConfig';
|
||||
|
||||
INSERT INTO sys_menu (
|
||||
id,
|
||||
parent_id,
|
||||
menu_name,
|
||||
path,
|
||||
menu_type,
|
||||
icon,
|
||||
create_user,
|
||||
update_user,
|
||||
sort,
|
||||
status,
|
||||
router,
|
||||
alias
|
||||
)
|
||||
SELECT
|
||||
1700000253,
|
||||
(SELECT id FROM sys_menu WHERE alias = 'ResidentActivityManager' LIMIT 1),
|
||||
'注册奖励配置',
|
||||
'resident-activity/register-reward-config/index',
|
||||
2,
|
||||
'form',
|
||||
'0',
|
||||
'0',
|
||||
2,
|
||||
0,
|
||||
'register-reward/config',
|
||||
'ResidentRegisterRewardConfig'
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM sys_menu WHERE alias = 'ResidentRegisterRewardConfig'
|
||||
);
|
||||
|
||||
UPDATE sys_menu
|
||||
SET
|
||||
parent_id = (
|
||||
SELECT resident_menu.id
|
||||
FROM (
|
||||
SELECT id
|
||||
FROM sys_menu
|
||||
WHERE alias = 'ResidentActivityManager'
|
||||
LIMIT 1
|
||||
) AS resident_menu
|
||||
),
|
||||
menu_name = '注册奖励配置',
|
||||
path = 'resident-activity/register-reward-config/index',
|
||||
menu_type = 2,
|
||||
icon = 'form',
|
||||
create_user = '0',
|
||||
update_user = '0',
|
||||
sort = 2,
|
||||
status = 0,
|
||||
router = 'register-reward/config'
|
||||
WHERE alias = 'ResidentRegisterRewardConfig';
|
||||
|
||||
INSERT INTO sys_menu (
|
||||
id,
|
||||
parent_id,
|
||||
menu_name,
|
||||
path,
|
||||
menu_type,
|
||||
icon,
|
||||
create_user,
|
||||
update_user,
|
||||
sort,
|
||||
status,
|
||||
router,
|
||||
alias
|
||||
)
|
||||
SELECT
|
||||
1700000254,
|
||||
(SELECT id FROM sys_menu WHERE alias = 'ResidentActivityManager' LIMIT 1),
|
||||
'邀请活动配置',
|
||||
'resident-activity/invite-config/index',
|
||||
2,
|
||||
'form',
|
||||
'0',
|
||||
'0',
|
||||
3,
|
||||
0,
|
||||
'invite/config',
|
||||
'ResidentInviteConfig'
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM sys_menu WHERE alias = 'ResidentInviteConfig'
|
||||
);
|
||||
|
||||
UPDATE sys_menu
|
||||
SET
|
||||
parent_id = (
|
||||
SELECT resident_menu.id
|
||||
FROM (
|
||||
SELECT id
|
||||
FROM sys_menu
|
||||
WHERE alias = 'ResidentActivityManager'
|
||||
LIMIT 1
|
||||
) AS resident_menu
|
||||
),
|
||||
menu_name = '邀请活动配置',
|
||||
path = 'resident-activity/invite-config/index',
|
||||
menu_type = 2,
|
||||
icon = 'form',
|
||||
create_user = '0',
|
||||
update_user = '0',
|
||||
sort = 3,
|
||||
status = 0,
|
||||
router = 'invite/config'
|
||||
WHERE alias = 'ResidentInviteConfig';
|
||||
|
||||
INSERT INTO sys_menu (
|
||||
id,
|
||||
parent_id,
|
||||
menu_name,
|
||||
path,
|
||||
menu_type,
|
||||
icon,
|
||||
create_user,
|
||||
update_user,
|
||||
sort,
|
||||
status,
|
||||
router,
|
||||
alias
|
||||
)
|
||||
SELECT
|
||||
1700000255,
|
||||
(SELECT id FROM sys_menu WHERE alias = 'ResidentActivityManager' LIMIT 1),
|
||||
'邀请活动列表',
|
||||
'resident-activity/invite-list/index',
|
||||
2,
|
||||
'form',
|
||||
'0',
|
||||
'0',
|
||||
4,
|
||||
0,
|
||||
'invite/list',
|
||||
'ResidentInviteList'
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM sys_menu WHERE alias = 'ResidentInviteList'
|
||||
);
|
||||
|
||||
UPDATE sys_menu
|
||||
SET
|
||||
parent_id = (
|
||||
SELECT resident_menu.id
|
||||
FROM (
|
||||
SELECT id
|
||||
FROM sys_menu
|
||||
WHERE alias = 'ResidentActivityManager'
|
||||
LIMIT 1
|
||||
) AS resident_menu
|
||||
),
|
||||
menu_name = '邀请活动列表',
|
||||
path = 'resident-activity/invite-list/index',
|
||||
menu_type = 2,
|
||||
icon = 'form',
|
||||
create_user = '0',
|
||||
update_user = '0',
|
||||
sort = 4,
|
||||
status = 0,
|
||||
router = 'invite/list'
|
||||
WHERE alias = 'ResidentInviteList';
|
||||
|
||||
INSERT INTO sys_menu (
|
||||
id,
|
||||
parent_id,
|
||||
menu_name,
|
||||
path,
|
||||
menu_type,
|
||||
icon,
|
||||
create_user,
|
||||
update_user,
|
||||
sort,
|
||||
status,
|
||||
router,
|
||||
alias
|
||||
)
|
||||
SELECT
|
||||
1700000256,
|
||||
(SELECT id FROM sys_menu WHERE alias = 'ResidentActivityManager' LIMIT 1),
|
||||
'指定礼物周榜',
|
||||
'resident-activity/week-star/index',
|
||||
2,
|
||||
'form',
|
||||
'0',
|
||||
'0',
|
||||
5,
|
||||
0,
|
||||
'specified-gift-weekly-rank',
|
||||
'ResidentSpecifiedGiftWeeklyRank'
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM sys_menu WHERE alias = 'ResidentSpecifiedGiftWeeklyRank'
|
||||
);
|
||||
|
||||
UPDATE sys_menu
|
||||
SET
|
||||
parent_id = (
|
||||
SELECT resident_menu.id
|
||||
FROM (
|
||||
SELECT id
|
||||
FROM sys_menu
|
||||
WHERE alias = 'ResidentActivityManager'
|
||||
LIMIT 1
|
||||
) AS resident_menu
|
||||
),
|
||||
menu_name = '指定礼物周榜',
|
||||
path = 'resident-activity/week-star/index',
|
||||
menu_type = 2,
|
||||
icon = 'form',
|
||||
create_user = '0',
|
||||
update_user = '0',
|
||||
sort = 5,
|
||||
status = 0,
|
||||
router = 'specified-gift-weekly-rank'
|
||||
WHERE alias = 'ResidentSpecifiedGiftWeeklyRank';
|
||||
|
||||
INSERT INTO sys_menu (
|
||||
id,
|
||||
parent_id,
|
||||
menu_name,
|
||||
path,
|
||||
menu_type,
|
||||
icon,
|
||||
create_user,
|
||||
update_user,
|
||||
sort,
|
||||
status,
|
||||
router,
|
||||
alias
|
||||
)
|
||||
SELECT
|
||||
1700000257,
|
||||
1700000057,
|
||||
'百顺游戏管理',
|
||||
'sys/game-config/baishun/index',
|
||||
2,
|
||||
'form',
|
||||
'0',
|
||||
'0',
|
||||
133,
|
||||
0,
|
||||
'baishun-game-manage',
|
||||
'OperateBaishunGameManage'
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM sys_menu WHERE alias = 'OperateBaishunGameManage'
|
||||
);
|
||||
|
||||
UPDATE sys_menu
|
||||
SET
|
||||
parent_id = 1700000057,
|
||||
menu_name = '百顺游戏管理',
|
||||
path = 'sys/game-config/baishun/index',
|
||||
menu_type = 2,
|
||||
icon = 'form',
|
||||
create_user = '0',
|
||||
update_user = '0',
|
||||
sort = 133,
|
||||
status = 0,
|
||||
router = 'baishun-game-manage'
|
||||
WHERE alias = 'OperateBaishunGameManage';
|
||||
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT DISTINCT
|
||||
parent_role.role_id,
|
||||
target_menu.id
|
||||
FROM sys_role_menu AS parent_role
|
||||
JOIN sys_menu AS target_menu
|
||||
ON target_menu.alias IN (
|
||||
'ResidentActivityManager',
|
||||
'ResidentSignInRewardConfig',
|
||||
'ResidentRegisterRewardConfig',
|
||||
'ResidentInviteConfig',
|
||||
'ResidentInviteList',
|
||||
'ResidentSpecifiedGiftWeeklyRank',
|
||||
'OperateBaishunGameManage'
|
||||
)
|
||||
LEFT JOIN sys_role_menu AS existing_role_menu
|
||||
ON existing_role_menu.role_id = parent_role.role_id
|
||||
AND existing_role_menu.menu_id = target_menu.id
|
||||
WHERE parent_role.menu_id = 1700000057
|
||||
AND existing_role_menu.id IS NULL;
|
||||
209
重构清单.md
209
重构清单.md
@ -1,209 +0,0 @@
|
||||
# webconsole UI 重构清单
|
||||
|
||||
## 页面文案约束
|
||||
|
||||
- 页面内禁止出现“重构、迁移、兼容、旧系统、承接页、占位、开发中”等与使用者无关的描述
|
||||
- 页面只保留业务信息、操作说明、状态提示
|
||||
- 本规则适用于当前页面与后续所有开发
|
||||
|
||||
## 统计结果
|
||||
|
||||
- 基础路由:5
|
||||
- 业务路由:144
|
||||
- 合计路由页:149
|
||||
|
||||
## 2026-03-31 筛选下拉审计
|
||||
|
||||
- 审计范围:`Frontend/apps/src/router/routes/modules` 对应的全部路由页,以及 [重构清单.md](/Users/zuozuo/Desktop/HY/AppPlatform/Frontend/重构清单.md) 中登记的 `149` 个页面范围
|
||||
- 路由组件实扫:`147` 个实际 `.vue` 路由页
|
||||
- 初始高风险页:`56` 个
|
||||
- 本轮批量修复:`50` 个路由页首轮收敛,剩余 `9` 个手工补齐
|
||||
- 当前残留:`0` 个“筛选区仍使用 `SelectOption` 且可能出现无文案回显”的路由页
|
||||
- 当前新增脚本:[fix-route-filter-selects.mjs](/Users/zuozuo/Desktop/HY/AppPlatform/Frontend/apps/scripts/fix-route-filter-selects.mjs#L1)
|
||||
- 本轮重点补齐:`用户银行流水` 的“类型/事件”,以及运营管理、系统管理、团队管理、动态管理、App 系统管理里剩余的筛选下拉文案回显
|
||||
- 本轮追加补齐:`用户银行卡提现现金申请`、`用户银行卡提现钻石申请` 的“卡片类型 / 类型 / 状态”,以及路由页里剩余的“类型”类下拉回显
|
||||
|
||||
## 基础路由(5项)
|
||||
|
||||
- [x] 登录页 | 路由: `/login`
|
||||
- [x] 404页 | 路由: `/404`
|
||||
- [x] 401页 | 路由: `/401`
|
||||
- [x] 首页 | 路由: `/dashboard`
|
||||
- [x] 用户详情 | 路由: `/common/user/deatils/:id`
|
||||
|
||||
## 团队管理(8项)
|
||||
|
||||
- [x] 团队政策 | 路由: `/team/team-policy`
|
||||
- [x] 团队钻石政策 | 路由: `/team/team-diamond-policy`
|
||||
- [x] 团队列表 | 路由: `/team/team-list`
|
||||
- [x] 成员列表 | 路由: `/team/team/member`
|
||||
- [x] 账单列表 | 路由: `/team/team/bill`
|
||||
- [x] 成员工作 | 路由: `/team/team/member-work`
|
||||
- [x] 成员审核日志 | 路由: `/team/team/application-process`
|
||||
- [x] BD Lead | 路由: `/team/team/bd-lead`
|
||||
|
||||
## 动态管理(4项)
|
||||
|
||||
- [x] 标签列表 | 路由: `/dynamic/manager/dynamic/tag`
|
||||
- [x] 权重配置 | 路由: `/dynamic/manager/dynamic/popular/config`
|
||||
- [x] 用户动态 | 路由: `/dynamic/manager/user/dynamic/list`
|
||||
- [x] 黑名单 | 路由: `/dynamic/manager/dynamic/blacklist`
|
||||
|
||||
## 家族管理(2项)
|
||||
|
||||
- [x] 家族配置 | 路由: `/family/manager/family_config`
|
||||
- [x] 家族列表 | 路由: `/family/manager/family_list`
|
||||
|
||||
## 宠物管理(4项)
|
||||
|
||||
- [x] 宠物喂养记录 | 路由: `/pet/manager/feeding`
|
||||
- [x] 豆子账户 | 路由: `/pet/manager/beans`
|
||||
- [x] 豆子流水 | 路由: `/pet/manager/beans/running-water`
|
||||
- [x] 宠物池 | 路由: `/pet/manager/pool`
|
||||
|
||||
## 道具管理(5项)
|
||||
|
||||
- [x] 道具赠送 | 路由: `/props/manager/props/send_tool`
|
||||
- [x] 资源配置 | 路由: `/props/manager/props/config`
|
||||
- [x] 道具资源组配置 | 路由: `/props/manager/props/props_source_group`
|
||||
- [x] 活动道具规则配置 | 路由: `/props/manager/props/props-activity-rule-config`
|
||||
- [x] 道具商店 | 路由: `/props/manager/props/props_store`
|
||||
|
||||
## 工具管理(5项)
|
||||
|
||||
- [x] 请求黑名单 | 路由: `/app/tools/request-blacklist`
|
||||
- [x] 金币分析 | 路由: `/app/tools/gold-analyze`
|
||||
- [x] Redis缓存管理 | 路由: `/redis`
|
||||
- [x] 文件上传 | 路由: `/upload/file`
|
||||
- [x] Socket测试 | 路由: `/socket`
|
||||
|
||||
## App系统管理(13项)
|
||||
|
||||
- [x] 登陆日志 | 路由: `/app/sys/mamange/login/logger`
|
||||
- [x] 房间黑名单 | 路由: `/app/sys/mamange/log/blacklist`
|
||||
- [x] 请求日志 | 路由: `/app/sys/mamange/log/api-request-log`
|
||||
- [x] 靓号管理 A | 路由: `/app/sys/mamange/userSpecialId`
|
||||
- [x] 靓号管理 B | 路由: `/app/sys/mamange/userSpecialId`
|
||||
- [x] 文档管理 | 路由: `/app/sys/mamange/external/app_doc_interface`
|
||||
- [x] IM通讯 | 路由: `/app/sys/mamange/external/im`
|
||||
- [x] 版本管理 | 路由: `/app/sys/mamange/version`
|
||||
- [x] 参数配置管理 | 路由: `/app/sys/mamange/enum/config`
|
||||
- [x] 参数配置 | 路由: `/app/sys/mamange/enum/setting`
|
||||
- [x] 内购产品配置V2 | 路由: `/app/sys/mamange/config/product/v2`
|
||||
- [x] 封禁设备 | 路由: `/app/sys/mamange/archive/device`
|
||||
- [x] 表情管理 | 路由: `/app/sys/mamange/emoji/config`
|
||||
|
||||
## 系统管理(5项)
|
||||
|
||||
- [x] 区域配置 | 路由: `/sys/manager/region/config`
|
||||
- [x] 用户管理 | 路由: `/sys/manager/user`
|
||||
- [x] 角色管理 | 路由: `/sys/manager/roles`
|
||||
- [x] 菜单管理 | 路由: `/sys/manager/menus`
|
||||
- [x] 资源管理 | 路由: `/sys/manager/resource`
|
||||
|
||||
## 统计管理(1项)
|
||||
|
||||
- [x] 数据大屏 | 路由: `/statistics/manager/datav`
|
||||
|
||||
## 运营管理(82项)
|
||||
|
||||
- [x] 代理活动 | 路由: `/operate/manager/agentActivity`
|
||||
- [x] 金币余额 | 路由: `/operate/manager/gold-balance`
|
||||
- [x] 自动发放工资凭据 | 路由: `/auto-salary-pay-record`
|
||||
- [x] 用户银行卡提现现金申请 | 路由: `/withdraw-money-apply-page`
|
||||
- [x] 用户银行卡提现钻石申请 | 路由: `/withdraw-diamond-apply-page`
|
||||
- [x] 用户银行金币兑换申请 | 路由: `/user-bank-withdraw-gold-apply`
|
||||
- [x] 用户银行卡钻石兑换金币申请 | 路由: `/user-bank-diamond-withdraw-gold-apply`
|
||||
- [x] 用户银行账户 | 路由: `/user-bank-balance`
|
||||
- [x] 用户银行流水 | 路由: `/user-running-water`
|
||||
- [x] 客服列表 | 路由: `/customer-service`
|
||||
- [x] CP申请 | 路由: `/operate/manager/cp`
|
||||
- [x] PK记录 | 路由: `/game/pk`
|
||||
- [x] 邀请用户日志 | 路由: `/operate/manager/invite-user-log`
|
||||
- [x] 活动配置 | 路由: `/operate/manager/activity/cnf`
|
||||
- [x] 活动模版 | 路由: `/operate/manager/activity/template`
|
||||
- [x] 用户友谊卡配置 | 路由: `/sys/friendship-card`
|
||||
- [x] 邀请用户配置 | 路由: `/sys/invite-user-rule`
|
||||
- [x] 用户观看广告配置 | 路由: `/sys/user-advertising`
|
||||
- [x] Ludo飞行棋 | 路由: `/game-ludo`
|
||||
- [x] 置顶房间 | 路由: `/operate/manager/set-top-room`
|
||||
- [x] 热门房间 | 路由: `/operate/manager/set-hot-room`
|
||||
- [x] 退款扣除目标 | 路由: `/operate/manager/refund-deduct-tag-log`
|
||||
- [x] 开通支付国家 A | 路由: `/operate/manager/pay/country`
|
||||
- [x] 支付区域关系 | 路由: `/operate/manager/region/relation`
|
||||
- [x] 应用管理 | 路由: `/operate/manager/pay/application`
|
||||
- [x] 支付厂商 | 路由: `/operate/manager/pay/factory`
|
||||
- [x] 支付渠道 | 路由: `/operate/manager/pay/channel`
|
||||
- [x] 开通支付国家 B | 路由: `/operate/manager/open_pay/country`
|
||||
- [x] 抽奖品配置 | 路由: `/operate/manager/cnf/lottery`
|
||||
- [x] 货运代理 | 路由: `/operate/manager/user_freight`
|
||||
- [x] BD列表 | 路由: `/operate/manager/room/business-development`
|
||||
- [x] 邀请用户奖励记录 | 路由: `/operate/manager/user/invite-reward`
|
||||
- [x] IM账号管理 | 路由: `/operate/manager/im/account/manager`
|
||||
- [x] 用户签到 | 路由: `/operate/manager/check/in/log`
|
||||
- [x] 用户列表 A(与 B 合并为同页双标签) | 路由: `/operate/manager/user`
|
||||
- [x] 用户列表 B(与 A 合并为同页双标签) | 路由: `/operate/manager/user`
|
||||
- [x] 用户最新设备列表 | 路由: `/operate/manager/user/user-device`
|
||||
- [x] APP管理员 | 路由: `/operate/manager/administrator`
|
||||
- [x] 订单补偿 | 路由: `/operate/manager/compensate`
|
||||
- [x] 小工具 | 路由: `/operate/manager/tools/other`
|
||||
- [x] 异常订单 | 路由: `/operate/manager/abnormal/log`
|
||||
- [x] 货运代理流水 | 路由: `/operate/manager/user_freight_info_list`
|
||||
- [x] 高产用户列表 | 路由: `/operate/manager/quality_users_list`
|
||||
- [x] 金币收支记录 | 路由: `/operate/manager/running/water`
|
||||
- [x] 内部操作金币 | 路由: `/operate/manager/inside-operation-gold`
|
||||
- [x] 游戏券收支记录 | 路由: `/game/coupon/running/water`
|
||||
- [x] 积分收支记录 | 路由: `/operate/manager/integral/stream`
|
||||
- [x] 订单详情 | 路由: `/operate/manager/order/details`
|
||||
- [x] 退款记录 | 路由: `/operate/manager/reimburse`
|
||||
- [x] 礼物管理 | 路由: `/operate/manager/gift`
|
||||
- [x] 抽奖概率配置 | 路由: `/game/lottery-config`
|
||||
- [x] 徽章配置管理 | 路由: `/operate/manager/badge`
|
||||
- [x] 靓号池管理 | 路由: `/operate/manager/beautiful-number`
|
||||
- [x] 礼物赠送记录 | 路由: `/operate/manager/gift/history`
|
||||
- [x] 房间贡献余额 | 路由: `/operate/manager/contribution/balance`
|
||||
- [x] 消息推送 | 路由: `/operate/manager/push`
|
||||
- [x] 在线房间 | 路由: `/operate/manager/online/room`
|
||||
- [x] 房间资料信息 | 路由: `/operate/manager/profile/room`
|
||||
- [x] 系统banner管理 | 路由: `/operate/manager/sys/banner`
|
||||
- [x] 麦位类型管理 | 路由: `/operate/manager/mike/type`
|
||||
- [x] 游戏列表 | 路由: `/operate/manager/game-config`
|
||||
- [x] App启动页 | 路由: `/operate/manager/sys/start/page`
|
||||
- [x] 系统公告管理 | 路由: `/operate/manager/notice/message`
|
||||
- [x] 国家管理 | 路由: `/operate/manager/sys/country`
|
||||
- [x] 用户道具流水 | 路由: `/running/water/user/props`
|
||||
- [x] 用户钻石余额列表 | 路由: `/user/diamond-balance`
|
||||
- [x] 用户钻石流水列表 | 路由: `/user/diamond-run-water`
|
||||
- [x] 购买麦位类型流水 | 路由: `/operate/manager/mike/type/list`
|
||||
- [x] 红包发送列表 | 路由: `/game/red/packet/record`
|
||||
- [x] 用户活动领奖记录 | 路由: `/user/user-activity-receive`
|
||||
- [x] 幸运礼物规则配置 | 路由: `/game/lucky-gift/rule-config/config`
|
||||
- [x] 幸运礼物规格配置 | 路由: `/game/lucky-gift/standard-config/config`
|
||||
- [x] 幸运礼物送礼记录 | 路由: `/game/lucky-gift/give-record`
|
||||
- [x] LuckyBox抽奖记录 | 路由: `/game/lucky-box`
|
||||
- [x] 摩天轮游戏每日数据 | 路由: `/game/fruit/log/fruit-days-log`
|
||||
- [x] 摩天轮任务配置 | 路由: `/operate/manager/game/fruit/task-config`
|
||||
- [x] 砸金蛋记录 | 路由: `/game/egg`
|
||||
- [x] 炸金花 | 路由: `/game/teen-patti`
|
||||
- [x] 用户申请靓号记录 | 路由: `/user/beautiful-number-apply`
|
||||
- [x] 自定义名人堂 | 路由: `/activity/hall-fame`
|
||||
- [x] 喇叭列表 | 路由: `/game/trumpet`
|
||||
- [x] 房间支持活动 | 路由: `/activity/room/contribution`
|
||||
|
||||
## 审批管理(15项)
|
||||
|
||||
- [x] 头像审核 | 路由: `/approval/manager/user/avatatphoto/approval`
|
||||
- [x] 昵称审核 | 路由: `/approval/manager/user/nickname/approval`
|
||||
- [x] 照片墙审核 | 路由: `/approval/manager/user/photowall/approval`
|
||||
- [x] 房间封面审批 | 路由: `/approval/manager/room/cover/approval`
|
||||
- [x] 用户个性签名审批 | 路由: `/approval/manager/user/profile_desc/approval`
|
||||
- [x] 房间通知审批 | 路由: `/approval/manager/room/notice/approval`
|
||||
- [x] 房间名称审批 | 路由: `/approval/manager/room/nickanme/approval`
|
||||
- [x] 房间主题审批 | 路由: `/approval/manager/room/theme/approval`
|
||||
- [x] 举报管理 | 路由: `/approval/manager/report`
|
||||
- [x] 意见反馈 | 路由: `/approval/manager/feedback`
|
||||
- [x] 违规历史记录 | 路由: `/approval/manager/not_pass_history/table`
|
||||
- [x] 家族头像审批 | 路由: `/approval/manager/family/approval`
|
||||
- [x] 动态内容审批 | 路由: `/approval/manager/dynamic/content/approval`
|
||||
- [x] 动态举报审批 | 路由: `/approval/manager/dynamic/report`
|
||||
- [x] 用户银行卡审批 | 路由: `/approval/manager/user/bank-card/approval`
|
||||
290
重构进度.md
290
重构进度.md
@ -1,290 +0,0 @@
|
||||
# webconsole UI 重构进度
|
||||
|
||||
## 当前总览
|
||||
|
||||
- 承载项目:`Frontend/`
|
||||
- 来源系统:`项目交接文件/webconsole_前端后管系统`
|
||||
- 启动日期:2026-03-30
|
||||
- 当前阶段:全部 149 个路由已完成;运营管理与统计管理均已按旧项目真实路由、接口和交互完成对齐
|
||||
- 设计基线:`Frontend/apps` 的 `vue-vben-admin 5.7` 风格
|
||||
- 重构范围:仅 UI 层
|
||||
- 总路由页:149
|
||||
- 基础路由:5
|
||||
- 业务路由:144
|
||||
- 已完成:149
|
||||
- 进行中:0
|
||||
- 未开始:0
|
||||
- 最近更新:2026-03-31
|
||||
|
||||
## 完成标准
|
||||
|
||||
- 页面布局、筛选区、工具栏、表格、分页、详情抽屉、编辑弹窗统一到 `Frontend/apps` 风格
|
||||
- 仅调整 UI 与交互呈现,不改接口字段、不改业务逻辑、不改权限点
|
||||
- 保持来源系统的 fullPath、菜单权限、接口调用兼容
|
||||
- 一个路由页只有在其直接依赖的抽屉 / 弹窗 / 编辑页样式同步完成后,才可记为“完成”
|
||||
|
||||
## 模块进度
|
||||
|
||||
| 模块 | 路由数 | 已完成 | 状态 | 备注 |
|
||||
| --- | ---: | ---: | --- | --- |
|
||||
| 基础路由 | 5 | 5 | 已完成 | 已完成旧路径兼容与 Frontend 风格承接 |
|
||||
| 团队管理 | 8 | 8 | 已完成 | 已对齐团队政策、团队钻石政策、团队列表、账单列表、成员列表、成员工作、成员审核日志、BD Lead |
|
||||
| 动态管理 | 4 | 4 | 已完成 | 已对齐标签、权重、用户动态、黑名单 |
|
||||
| 家族管理 | 2 | 2 | 已完成 | 已对齐家族配置三标签页、家族列表、成员弹层与解散/移除操作 |
|
||||
| 宠物管理 | 4 | 4 | 已完成 | 已对齐喂养记录、豆子账户、豆子流水、宠物池 |
|
||||
| 道具管理 | 5 | 5 | 已完成 | 已对齐赠送、资源配置、资源组、活动规则、商店 |
|
||||
| 工具管理 | 5 | 5 | 已完成 | 已对齐请求黑名单、金币分析、Redis缓存管理、文件上传、Socket测试 |
|
||||
| App系统管理 | 13 | 13 | 已完成 | 已对齐登陆日志、房间黑名单、请求日志、靓号管理、文档管理、IM通讯、版本管理、参数配置管理、参数配置、内购产品配置V2、封禁设备、表情管理 |
|
||||
| 系统管理 | 5 | 5 | 已完成 | 已对齐区域配置、用户、角色、菜单、资源的真实接口与操作 |
|
||||
| 统计管理 | 1 | 1 | 已完成 | 已对齐数据大屏的购买用户滚动列表、今日付费预览、Likei 在线统计与双击全屏交互 |
|
||||
| 运营管理 | 82 | 82 | 已完成 | 已对齐银行相关、客服/邀请/广告/友谊卡、CP申请、PK记录、房间运营、支付配置、应用配置、工具与奖励记录、活动配置与活动模版、抽奖品配置、用户列表、礼物管理、抽奖概率配置、徽章配置管理、房间资料信息、砸金蛋,以及管理员/国家/麦位/在线房间、货运代理、订单与礼物记录、IM账号、金币/游戏券流水、靓号池管理、游戏列表、房间贡献余额、消息推送、红包发送、幸运礼物、LuckyBox、炸金花、自定义名人堂、BD列表、摩天轮每日数据与任务配置;旧项目重复 `/operate/manager/user` 已按同页双标签收口 |
|
||||
| 审批管理 | 15 | 15 | 已完成 | 已按旧项目真实接口、筛选项、批量审批与弹层动作对齐 |
|
||||
|
||||
## 建议重构顺序
|
||||
|
||||
1. 基础路由
|
||||
2. 审批管理
|
||||
3. 系统管理 + App系统管理 + 工具管理
|
||||
4. 团队管理 + 动态管理 + 家族管理 + 宠物管理 + 道具管理
|
||||
5. 运营管理
|
||||
|
||||
## 当前风险
|
||||
|
||||
- 存在重复完整路由:
|
||||
- `/app/sys/mamange/userSpecialId`
|
||||
- `/operate/manager/user` 已按同页双标签承载两个旧页面
|
||||
- 存在历史拼写路由,迁移阶段暂不修正:
|
||||
- `/app/sys/mamange`
|
||||
- `/approval/manager/user/avatatphoto/approval`
|
||||
- `/approval/manager/room/nickanme/approval`
|
||||
- `/common/user/deatils/:id`
|
||||
- 存在绝对路径业务页,不属于父模块前缀下的相对路由,迁移时必须按真实 fullPath 保持兼容
|
||||
|
||||
## 更新规则
|
||||
|
||||
- 每次完成一个或多个路由页后,必须同步更新:
|
||||
- “当前总览”中的已完成 / 进行中 / 未开始数量
|
||||
- “模块进度”中的对应模块完成数与状态
|
||||
- “更新日志”中的本次完成内容
|
||||
|
||||
## 更新日志
|
||||
|
||||
### 2026-03-30
|
||||
|
||||
- 已将重构承载项目切换到 `Frontend/`
|
||||
- 已在 `Frontend/` 初始化设计上下文、重构清单和进度文档
|
||||
- 已添加基础路由兼容模块 `apps/src/router/routes/modules/legacy-core.ts`
|
||||
- 已完成登录页 `/login` 的 Frontend 登录页收敛,关闭多余演示入口
|
||||
- 已确认 `/401` `/404` 复用 Frontend 异常页完成承接
|
||||
- 已完成旧首页 `/dashboard` 的 Frontend 承接页 `apps/src/views/legacy/dashboard/index.vue`
|
||||
- 已完成旧用户详情 `/common/user/deatils/:id` 的详情页 UI 重构
|
||||
- 已清理重复 `/dashboard` 路由定义,并将默认首页切换到 `/dashboard`
|
||||
- 已新增审批路由模块 `apps/src/router/routes/modules/approval.ts`
|
||||
- 已将 `Frontend/apps` 请求层切换到旧项目 `/console` 地址、旧响应结构与旧鉴权头
|
||||
- 已将审批管理 15 个路由页改为旧项目真实接口、真实筛选参数与真实批量审批逻辑
|
||||
- 已补齐审批页依赖的编辑用户、账号处理、反馈处理弹层
|
||||
- 已将旧用户详情 `/common/user/deatils/:id` 改为真实接口数据页
|
||||
- 已将登录页改为用户名 / 密码表单,去除额外登录字段,保持旧项目校验规则与跳转方式
|
||||
- 已将登录链路调整为先 `/account/login` 获取 token,再由路由守卫按旧顺序拉取 `/account/info`、按钮权限与菜单
|
||||
- 已补充登录初始化失败时的清理与回登录页逻辑,避免 `info` 请求异常后残留无效登录态
|
||||
- 已新增系统管理路由模块 `apps/src/router/routes/modules/system.ts`
|
||||
- 已完成区域配置 `/sys/manager/region/config` 的区域列表与辅助配置双标签页
|
||||
- 已完成用户管理 `/sys/manager/user` 的查询、添加、修改、状态切换与重置密码
|
||||
- 已完成角色管理 `/sys/manager/roles` 的查询、添加、修改、删除与菜单授权
|
||||
- 已完成菜单管理 `/sys/manager/menus` 的菜单树、节点增改删与资源绑定
|
||||
- 已完成资源管理 `/sys/manager/resource` 的查询与刷新资源
|
||||
- 已新增 App系统管理路由模块 `apps/src/router/routes/modules/app-system.ts`
|
||||
- 已完成登陆日志 `/app/sys/mamange/login/logger` 的查询、分页、违规记录、账号处理记录与账号处理
|
||||
- 已完成房间黑名单 `/app/sys/mamange/log/blacklist` 的系统筛选、按 `lastId` 加载更多与房间详情查看
|
||||
- 已完成请求日志 `/app/sys/mamange/log/api-request-log` 的查询筛选与列表展示
|
||||
- 已完成重复路由 `/app/sys/mamange/userSpecialId` 的靓号管理页,对齐管理列表、操作日志、添加、改号、改有效期、自定义属性与删除
|
||||
- 已完成文档管理 `/app/sys/mamange/external/app_doc_interface` 与 IM通讯 `/app/sys/mamange/external/im` 的外链页接入
|
||||
- 已完成版本管理 `/app/sys/mamange/version` 的系统筛选、平台渠道切换、版本增改删、服务强更与多语言更新描述
|
||||
- 已完成参数配置管理 `/app/sys/mamange/enum/config` 的系统筛选、分组筛选、增改删与数据类型校验
|
||||
- 已完成参数配置 `/app/sys/mamange/enum/setting` 的分组切换、布尔开关编辑、普通参数编辑与拖拽排序保存
|
||||
- 已完成内购产品配置V2 `/app/sys/mamange/config/product/v2` 的系统筛选、平台/区域筛选与新增修改
|
||||
- 已完成封禁设备 `/app/sys/mamange/archive/device` 的查询、分页与解封操作
|
||||
- 已完成表情管理 `/app/sys/mamange/emoji/config` 的分组/资源双标签、上下架切换、OSS上传与新增修改
|
||||
- 已新增工具管理路由模块 `apps/src/router/routes/modules/tools.ts`
|
||||
- 已完成请求黑名单 `/app/tools/request-blacklist` 的黑名单查询、新增与删除
|
||||
- 已完成金币分析 `/app/tools/gold-analyze` 的时间区间、系统来源、用户维度图表分析
|
||||
- 已完成 Redis缓存管理 `/redis` 的缓存查询入口与结果展示
|
||||
- 已完成文件上传 `/upload/file` 的拖拽上传、多文件上传、原文件名切换、按文件类型分桶与链接复制
|
||||
- 已完成 Socket测试 `/socket` 的广播订阅、点对点订阅、消息发送、仓库写入与连接状态展示
|
||||
- 已新增动态管理路由模块 `apps/src/router/routes/modules/dynamic.ts`
|
||||
- 已完成标签列表 `/dynamic/manager/dynamic/tag` 的系统/状态/语言筛选、标签新增修改与背景图上传
|
||||
- 已完成权重配置 `/dynamic/manager/dynamic/popular/config` 的热门权重、等级限制、数量限制与金币配置修改
|
||||
- 已完成用户动态 `/dynamic/manager/user/dynamic/list` 的系统/置顶/时间/用户筛选、批量删除、置顶编辑、取消置顶、账号处理与用户详情跳转
|
||||
- 已完成黑名单 `/dynamic/manager/dynamic/blacklist` 的系统/用户筛选、加入黑名单、删除与用户详情跳转
|
||||
- 已新增团队管理路由模块 `apps/src/router/routes/modules/team.ts`
|
||||
- 已完成成员审核日志 `/team/team/application-process` 的系统/类型/原因/申请人筛选与按 `lastId` 加载更多
|
||||
- 已完成 BD Lead `/team/team/bd-lead` 的系统/用户筛选、分页、创建、编辑、删除与绑定子级BD
|
||||
- 已完成成员工作 `/team/team/member-work` 的系统/账单/代理/成员/时间筛选、按 `lastId` 加载更多、礼物流水查询与详情抽屉
|
||||
- 已补齐成员工作依赖的礼物流水弹层与礼物详情抽屉
|
||||
- 已补齐团队模块通用账号输入组件,对齐旧项目 `account-input` 的账号编码筛选逻辑
|
||||
- 已将成员审核日志、BD Lead、成员工作切换为旧项目账号筛选参数格式
|
||||
- 已完成成员列表 `/team/team/member` 的系统/角色/代理/成员/时间筛选、批量删除、添加成员、更换团队、修改备注与重置目标
|
||||
- 已补齐成员列表依赖的添加成员、更换团队、备注编辑、目标重置弹层
|
||||
- 已完成团队列表 `/team/team-list` 的系统/区域/状态/代理/成员数量/时间筛选、按 `lastId` 加载更多、批量删除与权限判断
|
||||
- 已补齐团队列表依赖的创建代理、编辑资料、备注、联系方式、资料详情、发送通知、通知记录、团队申请、绑定BD弹层
|
||||
- 已补齐团队列表所需的国家选择、团队通知、团队详情与申请记录真实接口
|
||||
- 已完成账单列表 `/team/team/bill` 的系统/区域/账单状态/代理/账单ID/时间筛选、按 `lastId` 加载更多与导出条件抽屉
|
||||
- 已补齐账单详情抽屉与账单导出下载逻辑,保持旧项目 `/team/bill/list` 与 `/team/bill/excel` 接口口径
|
||||
- 当前 UI 重构完成数:53 / 149
|
||||
|
||||
### 2026-03-31
|
||||
|
||||
- 已完成团队政策 `/team/team-policy` 的系统/区域/国家筛选、当前发布政策查看、历史政策列表、创建政策、保存、发布、删除与道具奖励编辑
|
||||
- 已完成团队钻石政策 `/team/team-diamond-policy` 的系统/区域筛选、当前发布政策查看、历史钻石政策列表、创建政策、保存、发布、删除与道具奖励编辑
|
||||
- 已补齐团队政策依赖的礼物、徽章、道具、支付国家与历史政策真实接口封装
|
||||
- 已新增团队政策共享奖励编辑抽屉,对齐旧项目的资源类型、数量/天数、描述、拖拽排序与保存口径
|
||||
- 已新增宠物管理路由模块 `apps/src/router/routes/modules/pet.ts`
|
||||
- 已完成宠物喂养记录 `/pet/manager/feeding` 的系统/用户/宠物筛选、分页与用户详情跳转
|
||||
- 已完成豆子账户 `/pet/manager/beans` 的用户筛选、余额列表、赠送/扣除弹窗与流水弹窗
|
||||
- 已完成豆子流水 `/pet/manager/beans/running-water` 的系统/类型/来源/用户/时间筛选,并支持余额页内嵌复用
|
||||
- 已完成宠物池 `/pet/manager/pool` 的系统/上架筛选、上下架切换、新增编辑、阶段配置、解锁条件、奖励组选择与资源上传
|
||||
- 已补齐宠物池依赖的奖励展示、奖励组选择抽屉与豆子操作弹窗,保持旧项目 `/pet_pool/*`、`/user-bean-balance/*`、`/props/activity/reward/group/page` 接口口径
|
||||
- 当前 UI 重构完成数:59 / 149
|
||||
|
||||
- 已新增运营管理路由模块 `apps/src/router/routes/modules/operate.ts`
|
||||
- 已完成金币余额 `/operate/manager/gold-balance` 的系统筛选、用户筛选、金币余额列表与按 `lastId` 加载更多
|
||||
- 已完成自动发放工资凭据 `/auto-salary-pay-record` 的系统筛选、代理/成员筛选、分页列表、详情、政策详情与备注抽屉
|
||||
- 已完成用户银行账户 `/user-bank-balance` 的系统/用户筛选、余额列表、创建账户、发送金额、扣除金额、转账、导入与单用户流水查看
|
||||
- 已完成用户银行流水 `/user-running-water` 的系统/类型/事件/用户/时间筛选、总额统计与按 `lastId` 加载更多
|
||||
- 已完成用户银行卡提现现金申请 `/withdraw-money-apply-page` 与用户银行卡提现钻石申请 `/withdraw-diamond-apply-page` 的系统/卡类型/用户/状态/时间筛选、详情、审核与导出条件抽屉
|
||||
- 已完成用户银行金币兑换申请 `/user-bank-withdraw-gold-apply` 与用户银行卡钻石兑换金币申请 `/user-bank-diamond-withdraw-gold-apply` 的系统/提交用户/接收用户/接收方式/时间筛选与按 `lastId` 加载更多
|
||||
- 已补齐运营管理首批页面依赖的用户银行、提现、兑换金币、自动工资凭据真实接口封装与共享弹层
|
||||
- 已新增道具管理路由模块 `apps/src/router/routes/modules/props.ts`
|
||||
- 已完成道具赠送 `/props/manager/props/send_tool` 的道具、徽章、道具券三类赠送表单,对齐旧项目权限点、资源加载与无效短账号反馈
|
||||
- 已完成资源配置 `/props/manager/props/config` 的系统/类型/状态筛选、上下架切换、新增修改、销售报表与销售概览
|
||||
- 已完成道具资源组配置 `/props/manager/props/props_source_group` 的系统/状态筛选、资源组编辑、奖励项排序与上下架切换
|
||||
- 已完成活动道具规则配置 `/props/manager/props/props-activity-rule-config` 的系统筛选、规则新增修改删除、资源组关联与特殊规则 JSON 配置
|
||||
- 已完成道具商店 `/props/manager/props/props_store` 的系统/类型/状态/付费类型筛选、商品新增修改与上下架切换
|
||||
- 已补齐道具管理依赖的资源上传、销量统计、首充奖励、游戏配置与商店真实接口封装,保持旧项目 `/props/source/record/*`、`/props/activity/*`、`/props/store/*`、`/count/operation/active-index/*` 接口口径
|
||||
- 当前 UI 重构完成数:72 / 149
|
||||
|
||||
- 已完成客服列表 `/customer-service` 的系统/区域筛选、客服列表、新增、修改、删除与区域/时间段配置
|
||||
- 已完成邀请用户日志 `/operate/manager/invite-user-log` 的系统/国家/邀请用户/被邀请用户/时间筛选与分页
|
||||
- 已完成用户友谊卡配置 `/sys/friendship-card` 的系统筛选、分页列表、新增修改、图片上传、上架状态与卡片金额配置
|
||||
- 已完成邀请用户配置 `/sys/invite-user-rule` 的 Lotfun 邀请有效天数与佣金比例配置
|
||||
- 已完成用户观看广告配置 `/sys/user-advertising` 的 Aswat 广告开关、每日上限、间隔秒数与金币奖励配置
|
||||
- 已补齐以上页面依赖的邀请用户、客服、友谊卡、邀请规则、广告配置真实接口封装
|
||||
- 当前 UI 重构完成数:77 / 149
|
||||
|
||||
- 已完成 Ludo飞行棋 `/game-ludo` 的运行中/历史记录切换、房间搜索、参与用户弹窗、房间详情与强制解散操作
|
||||
- 已完成置顶房间 `/operate/manager/set-top-room` 的系统/区域/房间筛选、创建修改、权重配置、有效期与房间详情
|
||||
- 已完成热门房间 `/operate/manager/set-hot-room` 的系统/房间筛选、创建修改、有效期与房间详情
|
||||
- 已完成退款扣除目标 `/operate/manager/refund-deduct-tag-log` 的系统/主播/退款人筛选与分页展示
|
||||
- 已补齐房间搜索、系统区域选择、Ludo、置顶/热门房间真实接口封装与复用弹层
|
||||
- 当前 UI 重构完成数:81 / 149
|
||||
|
||||
- 已完成 CP申请 `/operate/manager/cp` 的状态、发送用户、接收用户筛选与分页展示
|
||||
- 已完成 PK记录 `/game/pk` 的房间PK/团队PK双标签、系统筛选、房间搜索、加载更多与房间详情
|
||||
- 已完成邀请用户奖励记录 `/operate/manager/user/invite-reward` 的用户/时间筛选与分页展示
|
||||
- 已完成用户签到 `/operate/manager/check/in/log` 的用户/时间筛选、签到奖励展示与分页
|
||||
- 已完成用户钻石余额列表 `/user/diamond-balance` 的用户筛选、余额分页与用户资料跳转
|
||||
- 已完成用户钻石流水列表 `/user/diamond-run-water` 的系统/用户/类型/来源筛选与分页展示
|
||||
- 已完成用户活动领奖记录 `/user/user-activity-receive` 的用户/时间筛选与分页展示
|
||||
- 已完成喇叭列表 `/game/trumpet` 的系统/状态/用户筛选、按 `lastId` 加载更多、删除与账号处理
|
||||
- 已补齐 CP、PK、邀请奖励、签到、钻石余额/流水、活动领奖、喇叭列表真实接口封装与房间PK子面板
|
||||
- 当前 UI 重构完成数:89 / 149
|
||||
|
||||
- 已完成开通支付国家 `/operate/manager/pay/country` 与 `/operate/manager/open_pay/country` 的系统筛选、分页列表、批量新增国家、汇率/币种/排序更新、上下架切换与国家关联渠道维护
|
||||
- 已完成支付区域关系 `/operate/manager/region/relation` 的系统筛选、区域关系分页、新增、修改与支付国家关联
|
||||
- 已完成支付厂商 `/operate/manager/pay/factory` 的分页列表、图标上传、新增修改与关联渠道配置
|
||||
- 已完成支付渠道 `/operate/manager/pay/channel` 的分页列表、图标上传、新增修改与渠道类型管理
|
||||
- 已补齐支付配置组依赖的支付国家、区域关系、厂商、渠道、国家渠道明细真实接口封装与共享弹层
|
||||
- 当前 UI 重构完成数:94 / 149
|
||||
|
||||
- 已完成应用管理 `/operate/manager/pay/application` 的应用卡片列表、支付链接复制、应用新增修改,以及商品管理 / 商品管理V2 双标签配置
|
||||
- 已完成用户最新设备列表 `/operate/manager/user/user-device` 的系统/用户/设备号/时间筛选、分页展示、设备号复制与用户详情跳转
|
||||
- 已完成订单补偿 `/operate/manager/compensate` 的订单补偿表单、糖果补偿表单与确认提交流程
|
||||
- 已完成小工具 `/operate/manager/tools/other` 的用户账户调换、API参数签名、Alias Method分析、令牌解析、Redis清Key、用户房间区域变动与注销用户恢复
|
||||
- 已完成异常订单 `/operate/manager/abnormal/log` 的系统/状态/平台/时间筛选、异常订单分页、单据复制与状态变更
|
||||
- 已补齐以上页面依赖的支付应用、支付商品、设备列表、订单异常、补偿订单与工具类真实接口封装
|
||||
- 当前 UI 重构完成数:99 / 149
|
||||
|
||||
- 已完成 APP管理员 `/operate/manager/administrator` 的管理员列表、状态切换、角色分配、权限分配、移除管理,以及权限资源列表的增改删
|
||||
- 已完成 国家管理 `/operate/manager/sys/country` 的国家列表、国家简称筛选、图标上传、置顶与启用状态切换、编辑保存
|
||||
- 已完成 麦位类型管理 `/operate/manager/mike/type` 的系统/收费/状态筛选、麦位图与图标上传、新增修改与删除
|
||||
- 已完成 购买麦位类型流水 `/operate/manager/mike/type/list` 的系统/用户/麦位类型/到期类型筛选与分页展示
|
||||
- 已完成 在线房间 `/operate/manager/online/room` 的系统/区域/房间筛选、在线房间卡片列表、在线成员抽屉、账号处理与房间详情
|
||||
- 已完成 房间支持活动 `/activity/room/contribution` 的房间账号筛选、分页列表、房间详情与用户资料跳转
|
||||
- 已补齐以上页面依赖的管理员、国家、麦位类型、在线房间、房间支持活动真实接口封装与共享弹层
|
||||
- 当前 UI 重构完成数:105 / 149
|
||||
|
||||
- 已完成 代理活动 `/operate/manager/agentActivity` 的周/月类型筛选、区域筛选与分页列表展示
|
||||
- 已完成 用户道具流水 `/running/water/user/props` 的来源搜索、道具ID、购买人/接收人、时间筛选与分页展示
|
||||
- 已完成 用户申请靓号记录 `/user/beautiful-number-apply` 的系统/用户筛选、同意/驳回处理与分页列表
|
||||
- 已完成 高产用户列表 `/operate/manager/quality_users_list` 的总预览、每月预览、备注、本月充值明细与每月明细弹层
|
||||
- 已完成 积分收支记录 `/operate/manager/integral/stream` 的用户/时间/来源筛选、30天提示与分页展示
|
||||
- 已新增统计管理路由模块 `apps/src/router/routes/modules/statistics.ts`
|
||||
- 已完成 数据大屏 `/statistics/manager/datav` 的深色大屏布局、购买用户滚动列表、今日付费预览、Likei 在线统计与双击全屏交互
|
||||
- 已补齐数据大屏依赖的 `/datav/*` 与今日付费统计真实接口封装,并复用旧项目头图资源 `office_efficiency_header_bg.png`
|
||||
- 当前 UI 重构完成数:149 / 149
|
||||
- 已完成 活动配置 `/operate/manager/activity/cnf` 的活动图片列表、新增修改、图片上传、删除与活动链接复制
|
||||
- 已完成 系统banner管理 `/operate/manager/sys/banner` 的系统/平台/状态/区域/国家筛选、三图上传、新增修改与内容跳转配置
|
||||
- 已完成 App启动页 `/operate/manager/sys/start/page` 的系统/状态/类型筛选、启动页列表、新增修改、图片上传与删除
|
||||
- 已完成 系统公告管理 `/operate/manager/notice/message` 的系统/状态/发布时间筛选、公告新增修改、封面上传、发布与上下架切换
|
||||
- 已补齐以上页面依赖的代理活动、活动图片、启动页、系统公告、Banner、靓号申请、高产用户、积分流水、用户道具流水真实接口封装与共享弹层
|
||||
- 当前 UI 重构完成数:114 / 149
|
||||
|
||||
- 已完成 货运代理 `/operate/manager/user_freight` 的系统/显示/经销商/状态/用户筛选、账户列表、状态切换、发货、扣款、流水与授权卖家管理
|
||||
- 已完成 IM账号管理 `/operate/manager/im/account/manager` 的用户搜索、账号新增、删除与重置密码
|
||||
- 已完成 货运代理流水 `/operate/manager/user_freight_info_list` 的系统/发送用户/接收用户/来源/时间筛选、分页与导出
|
||||
- 已完成 金币收支记录 `/operate/manager/running/water` 的多系统筛选、来源搜索、用户/跟踪ID/记录ID/时间筛选与按 context 加载更多
|
||||
- 已完成 内部操作金币 `/operate/manager/inside-operation-gold` 的用户收支 / 货运流水双标签承接
|
||||
- 已完成 游戏券收支记录 `/game/coupon/running/water` 的多系统筛选、平台/类型/来源/用户/时间筛选与分页
|
||||
- 已完成 订单详情 `/operate/manager/order/details` 的多系统筛选、支付平台/状态/订单类型/用户/订单号/地区/时间筛选、统计汇总与详情抽屉
|
||||
- 已完成 退款记录 `/operate/manager/reimburse` 的多系统筛选、来源平台/用户/时间筛选、退款列表与详情抽屉
|
||||
- 已完成 礼物赠送记录 `/operate/manager/gift/history` 的系统/发送用户/接收用户/礼物ID/时间筛选、总价值统计、接收数量统计、详情抽屉与房间详情跳转
|
||||
- 已完成 靓号池管理 `/operate/manager/beautiful-number` 的状态/靓号/分组筛选、列表、新增、修改与删除
|
||||
- 已完成 游戏列表 `/operate/manager/game-config` 的系统/游戏源/状态筛选、房间游戏列表、新增修改、封面上传、删除与摩天轮数据配置跳转
|
||||
- 已补齐以上页面依赖的货运代理、货运流水、IM账号、游戏券流水、金币流水、订单明细、礼物记录、靓号池与游戏列表真实接口封装与详情抽屉
|
||||
- 当前 UI 重构完成数:125 / 149
|
||||
|
||||
- 已完成 活动配置 `/operate/manager/activity/cnf` 的活动列表、活动新增修改、活动复制、排行榜查看、奖品发送记录与活动链接复制,并按原项目 `template-cnf` 结构接回真实活动接口
|
||||
- 已完成 活动模版 `/operate/manager/activity/template` 的模版列表、名称编辑、模版复制,以及模版 `indexTree` JSON 编辑保存
|
||||
- 已完成 抽奖品配置 `/operate/manager/cnf/lottery` 的按系统加载/保存完整转盘 `body` 配置结构
|
||||
- 已完成 用户列表 `/operate/manager/user` 的旧 `user-table` 与 `user-table-role` 双页面同路由收口,保留双标签页、用户资料、账号处理、认证信息、重置密码与金币/钻石/游戏券操作
|
||||
- 已完成 礼物管理 `/operate/manager/gift` 的礼物配置、周星礼物配置、CP组合赠送礼物三标签页,以及礼物新增修改、区域/规格/特效/资源上传与上下架切换
|
||||
- 已完成 抽奖概率配置 `/game/lottery-config` 的分组列表与完整奖励组 JSON 编辑保存
|
||||
- 已完成 徽章配置管理 `/operate/manager/badge` 的规则、赠送/收回三标签页,以及徽章规则新增修改、资源上传、赠送与收回操作
|
||||
- 已完成 房间资料信息 `/operate/manager/profile/room` 的房间资料列表、成员/访客/日志抽屉、房间编辑、审批与黑名单/权限操作
|
||||
- 已完成 砸金蛋 `/game/egg` 的碎片兑换、抽奖记录、配置三标签页,以及抽取率、奖池和奖励配置维护
|
||||
- 已完成 运营管理收尾路由接入,修正 `/operate/manager/activity/cnf` 历史错配,并补齐 `/operate/manager/activity/template`、`/operate/manager/cnf/lottery`、`/operate/manager/gift`、`/operate/manager/badge`、`/operate/manager/profile/room`、`/game/lottery-config`、`/game/egg`
|
||||
- 当前 UI 重构完成数:148 / 149
|
||||
|
||||
### 2026-03-31
|
||||
|
||||
- 已新增家族管理路由模块 `apps/src/router/routes/modules/family.ts`
|
||||
- 已完成 家族配置 `/family/manager/family_config` 的家族等级配置、创建家族规则、创建每周奖励规则三标签页承接
|
||||
- 已完成 家族配置依赖的等级列表、创建规则、奖励规则真实接口封装,以及头像框/徽章/礼物选项和背景图上传
|
||||
- 已完成 家族列表 `/family/manager/family_list` 的系统/族长/家族账号/时间筛选、家族列表分页、成员列表弹层、解散家族与移除成员操作
|
||||
- 已补齐家族列表与家族成员真实接口封装,保持旧项目筛选参数与动作接口一致
|
||||
- 当前 UI 重构完成数:127 / 149
|
||||
|
||||
- 已完成 房间贡献余额 `/operate/manager/contribution/balance` 的总预览/时间段预览双标签、房间搜索、贡献余额展示与房间详情抽屉
|
||||
- 已完成 消息推送 `/operate/manager/push` 的新建Push、Push日志、推送任务三标签页,以及任务新增修改删除
|
||||
- 已完成 自定义名人堂 `/activity/hall-fame` 的名人堂列表、多语言文案、增改删与语言描述维护
|
||||
- 已完成 红包发送列表 `/game/red/packet/record` 的系统/类型/房间/用户/时间筛选、按 `lastId` 加载更多与详情抽屉
|
||||
- 已完成 幸运礼物规则配置 `/game/lucky-gift/rule-config/config` 的系统切换与大中小奖区间规则保存
|
||||
- 已完成 幸运礼物送礼记录 `/game/lucky-gift/give-record` 的系统/数量/礼物/用户/房间/时间筛选、统计汇总与分页展示
|
||||
- 已完成 LuckyBox抽奖记录 `/game/lucky-box` 的系统/用户/时间筛选、奖池/盈亏摘要、比例调整与详情抽屉
|
||||
- 已完成 炸金花 `/game/teen-patti` 的记录/配置双标签、开奖记录、下注用户抽屉与配置保存
|
||||
- 已完成 BD列表 `/operate/manager/room/business-development` 的 BD Lead 页面承接,沿用当前已对齐的系统/用户筛选、创建、编辑、删除与子级BD绑定逻辑
|
||||
- 已完成 幸运礼物规格配置 `/game/lucky-gift/standard-config/config` 的规格列表、增改删与 1/7/77/177/777 概率配置抽屉
|
||||
- 已完成 摩天轮游戏每日数据 `/game/fruit/log/fruit-days-log` 的每日数据/用户数据双标签、服务端排序、汇总信息与用户筛选
|
||||
- 已完成 摩天轮任务配置 `/operate/manager/game/fruit/task-config` 的赏金任务列表、任务配置抽屉、奖项图片加载、资源组选择与资源组配置入口
|
||||
- 已核对发现此前记录为完成的 活动配置 `/operate/manager/activity/cnf` 实际仍指向旧项目不对应页面,已从完成统计回退,待按原项目 `views/activity/template-cnf/index` 重新重构
|
||||
- 当前 UI 重构完成数:138 / 149
|
||||
|
||||
## 单次更新模板
|
||||
|
||||
### YYYY-MM-DD
|
||||
|
||||
- 完成模块:
|
||||
- 完成路由:
|
||||
- 累计完成:
|
||||
- 当前进行中:
|
||||
- 风险 / 阻塞:
|
||||
Loading…
x
Reference in New Issue
Block a user