修改商店

This commit is contained in:
zhx 2026-05-18 18:55:40 +08:00
parent 9ad05f8aef
commit 15fbfa95de
4 changed files with 88 additions and 51 deletions

View File

@ -85,11 +85,11 @@ export async function getGroupActivityList() {
} }
export async function sendPropsGiveUser(data: Record<string, any>) { export async function sendPropsGiveUser(data: Record<string, any>) {
return requestClient.post<string | null>('/props/give/send', data); return requestClient.post<null | string>('/props/give/send', data);
} }
export async function sendPropsTicketGiveUser(data: Record<string, any>) { export async function sendPropsTicketGiveUser(data: Record<string, any>) {
return requestClient.post<string | null>('/props/give/grant-coupon', data); return requestClient.post<null | string>('/props/give/grant-coupon', data);
} }
export async function pagePropsStore(params: Record<string, any>) { export async function pagePropsStore(params: Record<string, any>) {
@ -101,6 +101,17 @@ export async function pagePropsStore(params: Record<string, any>) {
); );
} }
export async function mapPropsStoreBySourceIds(data: Record<string, any>) {
return requestClient.post<Record<string, Record<string, any>>>(
'/go/props/store/source-map',
data,
);
}
export async function addOrUpdatePropsStore(data: Record<string, any>) { export async function addOrUpdatePropsStore(data: Record<string, any>) {
return requestClient.post('/go/props/store/add-or-update', data);
}
export async function addOrUpdatePropsStoreLegacy(data: Record<string, any>) {
return requestClient.post('/props/store/add-or-update', data); return requestClient.post('/props/store/add-or-update', data);
} }

View File

@ -1,28 +1,28 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, reactive, ref, watch } from 'vue'; import { computed, reactive, ref, watch } from 'vue';
import {
addOrUpdatePropsStore,
listSysOriginTypeList,
} from '#/api/legacy/props';
import { import {
Form, Form,
FormItem, FormItem,
Input, Input,
message,
Modal, Modal,
Select, Select,
Switch, Switch,
message,
} from 'antdv-next'; } from 'antdv-next';
import RewardIcon from './reward-icon.vue'; import {
addOrUpdatePropsStoreLegacy,
listSysOriginTypeList,
} from '#/api/legacy/props';
import { import {
NOBLE_VIP_OPTIONS, NOBLE_VIP_OPTIONS,
PROPS_CURRENCY_TYPES, PROPS_CURRENCY_TYPES,
PROPS_STORE_TYPES, PROPS_STORE_TYPES,
PROPS_VALID_DAYS, PROPS_VALID_DAYS,
} from '../shared'; } from '../shared';
import RewardIcon from './reward-icon.vue';
const props = defineProps<{ const props = defineProps<{
open: boolean; open: boolean;
@ -42,14 +42,15 @@ const sourceOptionsMap = reactive<
Record<string, { list: Array<Record<string, any>>; loaded: boolean; loading: boolean }> Record<string, { list: Array<Record<string, any>>; loaded: boolean; loading: boolean }>
>({}); >({});
const mainSource = ref<Record<string, any> | null>(null); const mainSource = ref<null | Record<string, any>>(null);
const rideSource = ref<Record<string, any> | null>(null); const rideSource = ref<null | Record<string, any>>(null);
const avatarSource = ref<Record<string, any> | null>(null); const avatarSource = ref<null | Record<string, any>>(null);
const dataCardSource = ref<Record<string, any> | null>(null); const dataCardSource = ref<null | Record<string, any>>(null);
const chatBubbleSource = ref<Record<string, any> | null>(null); const chatBubbleSource = ref<null | Record<string, any>>(null);
const form = reactive<Record<string, any>>({ const form = reactive<Record<string, any>>({
currencyTypes: '', currencyTypes: '',
del: false,
discount: 0, discount: 0,
id: '', id: '',
label: '', label: '',
@ -161,6 +162,7 @@ watch(
const propsAbility = record.propsAbility || {}; const propsAbility = record.propsAbility || {};
const nobleVipAbility = propsAbility.nobleVipAbility || {}; const nobleVipAbility = propsAbility.nobleVipAbility || {};
form.currencyTypes = commodity.currencyTypes || ''; form.currencyTypes = commodity.currencyTypes || '';
form.del = commodity.del ?? false;
form.discount = commodity.discount || 0; form.discount = commodity.discount || 0;
form.id = commodity.id || ''; form.id = commodity.id || '';
form.label = commodity.label || ''; form.label = commodity.label || '';
@ -203,7 +205,7 @@ watch(
{ immediate: true }, { immediate: true },
); );
function updateSelectedSource(type: string, id: string | number) { function updateSelectedSource(type: string, id: number | string) {
const item = sourceOptionsMap[type]?.list.find( const item = sourceOptionsMap[type]?.list.find(
(option) => String(option.id) === String(id), (option) => String(option.id) === String(id),
); );
@ -295,7 +297,7 @@ async function handleSubmit() {
} }
saving.value = true; saving.value = true;
try { try {
await addOrUpdatePropsStore({ await addOrUpdatePropsStoreLegacy({
...form, ...form,
currencyTypes: form.tmpCurrencyTypes.join(','), currencyTypes: form.tmpCurrencyTypes.join(','),
discount: Number(form.discount) >= 0 && Number(form.discount) <= 1 ? Number(form.discount) : 1, discount: Number(form.discount) >= 0 && Number(form.discount) <= 1 ? Number(form.discount) : 1,
@ -324,9 +326,11 @@ async function handleSubmit() {
<Form layout="vertical"> <Form layout="vertical">
<div class="form-grid"> <div class="form-grid">
<FormItem label="归属系统"> <FormItem label="归属系统">
<SysOriginSelect v-model:value="form.sysOrigin" disabled <SysOriginSelect
v-model:value="form.sysOrigin"
disabled
:options="sysOriginOptions" :options="sysOriginOptions"
></SysOriginSelect> />
</FormItem> </FormItem>
<FormItem label="道具类型"> <FormItem label="道具类型">
<Select <Select
@ -339,7 +343,8 @@ async function handleSubmit() {
</div> </div>
<FormItem label="道具资源"> <FormItem label="道具资源">
<Select option-label-prop="label" <Select
option-label-prop="label"
v-model:value="form.sourceId" v-model:value="form.sourceId"
:loading="sourceOptionsMap[form.propsType]?.loading" :loading="sourceOptionsMap[form.propsType]?.loading"
:options="getSourceSelectOptions(form.propsType)" :options="getSourceSelectOptions(form.propsType)"
@ -429,7 +434,8 @@ async function handleSubmit() {
<div class="ability-grid"> <div class="ability-grid">
<div class="ability-item"> <div class="ability-item">
<div class="ability-item__label">车辆</div> <div class="ability-item__label">车辆</div>
<Select option-label-prop="label" <Select
option-label-prop="label"
v-model:value="form.propsAbility.carId" v-model:value="form.propsAbility.carId"
:loading="sourceOptionsMap.RIDE?.loading" :loading="sourceOptionsMap.RIDE?.loading"
:options="getSourceSelectOptions('RIDE')" :options="getSourceSelectOptions('RIDE')"
@ -447,7 +453,8 @@ async function handleSubmit() {
<div class="ability-item"> <div class="ability-item">
<div class="ability-item__label">头像框</div> <div class="ability-item__label">头像框</div>
<Select option-label-prop="label" <Select
option-label-prop="label"
v-model:value="form.propsAbility.avatarFrameId" v-model:value="form.propsAbility.avatarFrameId"
:loading="sourceOptionsMap.AVATAR_FRAME?.loading" :loading="sourceOptionsMap.AVATAR_FRAME?.loading"
:options="getSourceSelectOptions('AVATAR_FRAME')" :options="getSourceSelectOptions('AVATAR_FRAME')"
@ -465,7 +472,8 @@ async function handleSubmit() {
<div class="ability-item"> <div class="ability-item">
<div class="ability-item__label">资料卡</div> <div class="ability-item__label">资料卡</div>
<Select option-label-prop="label" <Select
option-label-prop="label"
v-model:value="form.propsAbility.dataCardId" v-model:value="form.propsAbility.dataCardId"
:loading="sourceOptionsMap.DATA_CARD?.loading" :loading="sourceOptionsMap.DATA_CARD?.loading"
:options="getSourceSelectOptions('DATA_CARD')" :options="getSourceSelectOptions('DATA_CARD')"
@ -483,7 +491,8 @@ async function handleSubmit() {
<div class="ability-item"> <div class="ability-item">
<div class="ability-item__label">聊天气泡</div> <div class="ability-item__label">聊天气泡</div>
<Select option-label-prop="label" <Select
option-label-prop="label"
v-model:value="form.propsAbility.chatBubbleId" v-model:value="form.propsAbility.chatBubbleId"
:loading="sourceOptionsMap.CHAT_BUBBLE?.loading" :loading="sourceOptionsMap.CHAT_BUBBLE?.loading"
:options="getSourceSelectOptions('CHAT_BUBBLE')" :options="getSourceSelectOptions('CHAT_BUBBLE')"

View File

@ -34,6 +34,7 @@ import {
import { import {
addOrUpdatePropsStore, addOrUpdatePropsStore,
mapPropsStoreBySourceIds,
offShelfPropsSource, offShelfPropsSource,
pagePropsSource, pagePropsSource,
updatePropsSource, updatePropsSource,
@ -258,7 +259,8 @@ async function loadData(reset = false) {
loading.value = true; loading.value = true;
try { try {
const result = await pagePropsSource({ ...query }); const result = await pagePropsSource({ ...query });
list.value = result.records || []; const records = result.records || [];
list.value = await attachStoreCommodities(records);
total.value = result.total || 0; total.value = result.total || 0;
} finally { } finally {
loading.value = false; loading.value = false;
@ -266,6 +268,27 @@ async function loadData(reset = false) {
} }
} }
async function attachStoreCommodities(records: Array<Record<string, any>>) {
const sourceIds = [
...new Set(
records
.map((record) => String(record.id || '').trim())
.filter(Boolean),
),
];
if (sourceIds.length === 0) {
return records;
}
const commodityMap = await mapPropsStoreBySourceIds({
sourceIds,
sysOrigin: query.sysOrigin,
});
return records.map((record) => ({
...record,
storeCommodity: commodityMap[String(record.id)] || null,
}));
}
function updateTableScrollY() { function updateTableScrollY() {
const area = tableAreaRef.value; const area = tableAreaRef.value;
if (!area) { if (!area) {
@ -423,6 +446,7 @@ function buildStoreShelfPayload(record: Record<string, any>, checked: boolean) {
return { return {
...commodity, ...commodity,
currencyTypes: commodity.currencyTypes || DEFAULT_STORE_CURRENCY_TYPES, currencyTypes: commodity.currencyTypes || DEFAULT_STORE_CURRENCY_TYPES,
del: commodity.del ?? false,
discount: commodity.discount ?? 0, discount: commodity.discount ?? 0,
label: commodity.label || '', label: commodity.label || '',
propsType: commodity.propsType || record.type, propsType: commodity.propsType || record.type,
@ -442,9 +466,6 @@ async function handleStoreShelfStatusChange(record: Record<string, any>, checked
try { try {
await addOrUpdatePropsStore(nextCommodity); await addOrUpdatePropsStore(nextCommodity);
message.success('道具商店状态已更新'); message.success('道具商店状态已更新');
if (!commodity) {
await loadData();
}
} catch (error) { } catch (error) {
if (commodity) { if (commodity) {
commodity.shelfStatus = previous; commodity.shelfStatus = previous;

View File

@ -1,35 +1,31 @@
<script lang="ts" setup> <script lang="ts" setup>
import { import { computed, reactive, ref } from 'vue';
computed,
reactive,
ref } from 'vue';
import { Page } from '@vben/common-ui'; import { Page } from '@vben/common-ui';
import { useAccessStore } from '@vben/stores'; import { useAccessStore } from '@vben/stores';
import {
addOrUpdatePropsStore,
pagePropsStore,
} from '#/api/legacy/props';
import SysOriginLabel from '#/components/sys-origin-label.vue';
import InlineFilterField from '#/views/_shared/inline-filter-field.vue';
import InlineFilterToolbar from '#/views/_shared/inline-filter-toolbar.vue';
import { formatDate,
getAllowedSysOrigins } from '#/views/system/shared';
import { import {
Button, Button,
Card, Card,
Input, Input,
message,
Pagination, Pagination,
Select, Select,
Space, Space,
Switch, Switch,
Table, Table,
Tag, Tag,
message
} from 'antdv-next'; } from 'antdv-next';
import {
addOrUpdatePropsStore,
pagePropsStore,
} from '#/api/legacy/props';
import SysOriginLabel from '#/components/sys-origin-label.vue';
import InlineFilterField from '#/views/_shared/inline-filter-field.vue';
import InlineFilterToolbar from '#/views/_shared/inline-filter-toolbar.vue';
import { formatDate, getAllowedSysOrigins } from '#/views/system/shared';
import RewardIcon from './components/reward-icon.vue'; import RewardIcon from './components/reward-icon.vue';
import StoreEditModal from './components/store-edit-modal.vue'; import StoreEditModal from './components/store-edit-modal.vue';
import { import {
@ -50,7 +46,7 @@ const loading = ref(false);
const total = ref(0); const total = ref(0);
const list = ref<Array<Record<string, any>>>([]); const list = ref<Array<Record<string, any>>>([]);
const formOpen = ref(false); const formOpen = ref(false);
const activeRow = ref<Record<string, any> | null>(null); const activeRow = ref<null | Record<string, any>>(null);
const query = reactive({ const query = reactive({
cursor: 1, cursor: 1,
@ -145,34 +141,34 @@ loadData(true);
@change="loadData(true)" @change="loadData(true)"
:options="sysOriginOptions" :options="sysOriginOptions"
></SysOriginSelect> />
</InlineFilterField> </InlineFilterField>
<InlineFilterField label="道具类型"> <InlineFilterField label="道具类型">
<Select option-label-prop="label" <Select
option-label-prop="label"
v-model:value="query.propsType" v-model:value="query.propsType"
style="width: 140px" style="width: 140px"
@change="loadData(true)" @change="loadData(true)"
:options="PROPS_STORE_TYPES.map((item) => ({ label: `${item.name}`, value: item.value as any }))" :options="PROPS_STORE_TYPES.map((item) => ({ label: `${item.name}`, value: item.value as any }))"
/> />
</InlineFilterField> </InlineFilterField>
<InlineFilterField label="状态"> <InlineFilterField label="状态">
<Select option-label-prop="label" <Select
option-label-prop="label"
v-model:value="query.shelfStatus" v-model:value="query.shelfStatus"
allow-clear allow-clear
style="width: 120px" style="width: 120px"
@change="loadData(true)" @change="loadData(true)"
:options="PROPS_SHELF_STATUS_OPTIONS.map((item) => ({ label: `${item.name}`, value: item.value as any }))" :options="PROPS_SHELF_STATUS_OPTIONS.map((item) => ({ label: `${item.name}`, value: item.value as any }))"
/> />
</InlineFilterField> </InlineFilterField>
<InlineFilterField label="付费类型" :label-width="72"> <InlineFilterField label="付费类型" :label-width="72">
<Select option-label-prop="label" <Select
option-label-prop="label"
v-model:value="query.currencyTypes" v-model:value="query.currencyTypes"
allow-clear allow-clear
style="width: 120px" style="width: 120px"
@change="loadData(true)" @change="loadData(true)"
:options="PROPS_CURRENCY_TYPES.map((item) => ({ label: `${item.name}`, value: item.value as any }))" :options="PROPS_CURRENCY_TYPES.map((item) => ({ label: `${item.name}`, value: item.value as any }))"
/> />
</InlineFilterField> </InlineFilterField>
@ -262,7 +258,7 @@ loadData(true);
:total="total" :total="total"
show-size-changer show-size-changer
@change="handlePageChange" @change="handlePageChange"
@showSizeChange="handlePageChange" @show-size-change="handlePageChange"
/> />
</div> </div>
</Card> </Card>