feat: inline edit resource store status
This commit is contained in:
parent
1b835ba52d
commit
fa5663bdbf
@ -1,6 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {
|
import {
|
||||||
computed,
|
computed,
|
||||||
|
nextTick,
|
||||||
reactive,
|
reactive,
|
||||||
ref,
|
ref,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
@ -13,6 +14,7 @@ import {
|
|||||||
Card,
|
Card,
|
||||||
Empty,
|
Empty,
|
||||||
Input,
|
Input,
|
||||||
|
InputNumber,
|
||||||
message,
|
message,
|
||||||
Pagination,
|
Pagination,
|
||||||
Select,
|
Select,
|
||||||
@ -20,11 +22,14 @@ import {
|
|||||||
Switch,
|
Switch,
|
||||||
Table,
|
Table,
|
||||||
Tag,
|
Tag,
|
||||||
|
Tooltip,
|
||||||
} from 'antdv-next';
|
} from 'antdv-next';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
addOrUpdatePropsStore,
|
||||||
offShelfPropsSource,
|
offShelfPropsSource,
|
||||||
pagePropsSource,
|
pagePropsSource,
|
||||||
|
updatePropsSource,
|
||||||
} from '#/api/legacy/props';
|
} from '#/api/legacy/props';
|
||||||
import InlineFilterField from '#/views/_shared/inline-filter-field.vue';
|
import InlineFilterField from '#/views/_shared/inline-filter-field.vue';
|
||||||
import InlineFilterToolbar from '#/views/_shared/inline-filter-toolbar.vue';
|
import InlineFilterToolbar from '#/views/_shared/inline-filter-toolbar.vue';
|
||||||
@ -64,6 +69,9 @@ const formOpen = ref(false);
|
|||||||
const salesOpen = ref(false);
|
const salesOpen = ref(false);
|
||||||
const overviewOpen = ref(false);
|
const overviewOpen = ref(false);
|
||||||
const activeRow = ref<null | Record<string, any>>(null);
|
const activeRow = ref<null | Record<string, any>>(null);
|
||||||
|
const amountEditingId = ref<null | string>(null);
|
||||||
|
const amountSavingId = ref<null | string>(null);
|
||||||
|
const amountDraft = ref<null | number>(null);
|
||||||
|
|
||||||
const query = reactive({
|
const query = reactive({
|
||||||
cursor: 1,
|
cursor: 1,
|
||||||
@ -83,6 +91,7 @@ const columns = [
|
|||||||
{ dataIndex: 'cover', key: 'cover', title: '资源', width: 120 },
|
{ dataIndex: 'cover', key: 'cover', title: '资源', width: 120 },
|
||||||
{ dataIndex: 'del', key: 'del', title: '上/下架', width: 120 },
|
{ dataIndex: 'del', key: 'del', title: '上/下架', width: 120 },
|
||||||
{ dataIndex: 'amount', key: 'amount', title: '底价', width: 100 },
|
{ dataIndex: 'amount', key: 'amount', title: '底价', width: 100 },
|
||||||
|
{ dataIndex: 'storeShelfStatus', key: 'storeShelfStatus', title: '是否上架道具商店', width: 160 },
|
||||||
{ dataIndex: 'adminFree', key: 'adminFree', title: 'Admin Free', width: 120 },
|
{ dataIndex: 'adminFree', key: 'adminFree', title: 'Admin Free', width: 120 },
|
||||||
{ dataIndex: 'actions', key: 'actions', title: '操作', width: 180 },
|
{ dataIndex: 'actions', key: 'actions', title: '操作', width: 180 },
|
||||||
];
|
];
|
||||||
@ -122,6 +131,108 @@ async function handleStatusChange(record: Record<string, any>, checked: boolean)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getRecordId(record: Record<string, any>) {
|
||||||
|
return String(record.id || '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAmountEditing(record: Record<string, any>) {
|
||||||
|
return amountEditingId.value === getRecordId(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAmountSaving(record: Record<string, any>) {
|
||||||
|
return amountSavingId.value === getRecordId(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
function startAmountEdit(record: Record<string, any>) {
|
||||||
|
if (!canEdit.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
amountEditingId.value = getRecordId(record);
|
||||||
|
amountDraft.value = Number(record.amount ?? 0);
|
||||||
|
void nextTick();
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelAmountEdit() {
|
||||||
|
amountEditingId.value = null;
|
||||||
|
amountDraft.value = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPropsSourceUpdatePayload(record: Record<string, any>, amount: number) {
|
||||||
|
return {
|
||||||
|
adminFree: Boolean(record.adminFree),
|
||||||
|
amount,
|
||||||
|
code: record.code,
|
||||||
|
cover: record.cover,
|
||||||
|
expand: record.expand,
|
||||||
|
id: record.id,
|
||||||
|
name: record.name,
|
||||||
|
sourceUrl: record.sourceUrl,
|
||||||
|
sysOrigin: record.sysOrigin,
|
||||||
|
type: record.type,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleAmountConfirm(record: Record<string, any>) {
|
||||||
|
if (!isAmountEditing(record) || isAmountSaving(record)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const amount = Number(amountDraft.value);
|
||||||
|
if (!Number.isFinite(amount) || amount < 0) {
|
||||||
|
message.warning('请输入正确的底价');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const previous = record.amount;
|
||||||
|
if (Number(previous ?? 0) === amount) {
|
||||||
|
cancelAmountEdit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
amountSavingId.value = getRecordId(record);
|
||||||
|
record.amount = amount;
|
||||||
|
try {
|
||||||
|
await updatePropsSource(buildPropsSourceUpdatePayload(record, amount));
|
||||||
|
message.success('底价已更新');
|
||||||
|
cancelAmountEdit();
|
||||||
|
} catch (error) {
|
||||||
|
record.amount = previous;
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
amountSavingId.value = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStoreCommodity(record: Record<string, any>) {
|
||||||
|
return record.storeCommodity || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildStoreShelfPayload(record: Record<string, any>, checked: boolean) {
|
||||||
|
const commodity = getStoreCommodity(record);
|
||||||
|
return {
|
||||||
|
...commodity,
|
||||||
|
label: commodity.label || '',
|
||||||
|
propsType: commodity.propsType || record.type,
|
||||||
|
shelfStatus: checked,
|
||||||
|
sourceId: commodity.sourceId || record.id,
|
||||||
|
sysOrigin: commodity.sysOrigin || record.sysOrigin,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleStoreShelfStatusChange(record: Record<string, any>, checked: boolean) {
|
||||||
|
const commodity = getStoreCommodity(record);
|
||||||
|
if (!commodity) {
|
||||||
|
message.warning('请先在道具商店配置该资源');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const previous = commodity.shelfStatus;
|
||||||
|
commodity.shelfStatus = checked;
|
||||||
|
try {
|
||||||
|
await addOrUpdatePropsStore(buildStoreShelfPayload(record, checked));
|
||||||
|
message.success('道具商店状态已更新');
|
||||||
|
} catch (error) {
|
||||||
|
commodity.shelfStatus = previous;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function openCreate() {
|
function openCreate() {
|
||||||
activeRow.value = null;
|
activeRow.value = null;
|
||||||
formOpen.value = true;
|
formOpen.value = true;
|
||||||
@ -203,7 +314,7 @@ loadData(true);
|
|||||||
:loading="loading"
|
:loading="loading"
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
row-key="id"
|
row-key="id"
|
||||||
:scroll="{ x: 1160 }"
|
:scroll="{ x: 1320 }"
|
||||||
>
|
>
|
||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
<template v-if="column.key === 'cover'">
|
<template v-if="column.key === 'cover'">
|
||||||
@ -217,6 +328,42 @@ loadData(true);
|
|||||||
@change="(checked: boolean) => handleStatusChange(record, checked)"
|
@change="(checked: boolean) => handleStatusChange(record, checked)"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-else-if="column.key === 'amount'">
|
||||||
|
<InputNumber
|
||||||
|
v-if="isAmountEditing(record)"
|
||||||
|
v-model:value="amountDraft"
|
||||||
|
autofocus
|
||||||
|
:disabled="isAmountSaving(record)"
|
||||||
|
:min="0"
|
||||||
|
size="small"
|
||||||
|
style="width: 92px"
|
||||||
|
@blur="() => handleAmountConfirm(record)"
|
||||||
|
@keydown.enter.prevent="() => handleAmountConfirm(record)"
|
||||||
|
@keydown.esc="cancelAmountEdit"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
v-else
|
||||||
|
:class="['amount-value', { 'amount-value--editable': canEdit }]"
|
||||||
|
@click="startAmountEdit(record)"
|
||||||
|
>
|
||||||
|
{{ record.amount ?? 0 }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.key === 'storeShelfStatus'">
|
||||||
|
<Tooltip
|
||||||
|
:title="getStoreCommodity(record) ? '' : '请先在道具商店配置该资源'"
|
||||||
|
>
|
||||||
|
<Switch
|
||||||
|
:checked="Boolean(getStoreCommodity(record)?.shelfStatus)"
|
||||||
|
:disabled="!getStoreCommodity(record)"
|
||||||
|
checked-children="上架"
|
||||||
|
un-checked-children="下架"
|
||||||
|
@change="
|
||||||
|
(checked: boolean) => handleStoreShelfStatusChange(record, checked)
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</template>
|
||||||
<template v-else-if="column.key === 'adminFree'">
|
<template v-else-if="column.key === 'adminFree'">
|
||||||
<Tag :color="record.adminFree ? 'green' : 'default'">
|
<Tag :color="record.adminFree ? 'green' : 'default'">
|
||||||
{{ record.adminFree ? '是' : '否' }}
|
{{ record.adminFree ? '是' : '否' }}
|
||||||
@ -286,4 +433,14 @@ loadData(true);
|
|||||||
.toolbar {
|
.toolbar {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.amount-value {
|
||||||
|
display: inline-block;
|
||||||
|
min-width: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount-value--editable {
|
||||||
|
color: #1677ff;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user