313 lines
8.7 KiB
Vue
313 lines
8.7 KiB
Vue
<template>
|
|
<div v-can="savePermissionCode" class="store-edit">
|
|
<t-form class="form" label-align="left" v-entrance-transition>
|
|
<t-form-item v-if="!isStoreManagerScope" label="编码">
|
|
<t-input v-model.trim="formData.code" placeholder="门店编码" borderless />
|
|
</t-form-item>
|
|
<t-form-item label="名称">
|
|
<t-input v-model.trim="formData.name" placeholder="门店名称" borderless />
|
|
</t-form-item>
|
|
<t-form-item v-if="!isStoreManagerScope" label="启用" content-align="right">
|
|
<t-switch v-model="formData.status" :custom-value="['ACTIVE', 'INACTIVE']" />
|
|
</t-form-item>
|
|
<t-form-item label="电话">
|
|
<t-input v-model.trim="formData.telephone" type="tel" placeholder="联系电话" borderless />
|
|
</t-form-item>
|
|
<t-form-item label="地址">
|
|
<t-input v-model.trim="formData.address" placeholder="门店地址" borderless />
|
|
</t-form-item>
|
|
<t-form-item label="备注">
|
|
<t-textarea v-model.trim="formData.remark" class="textarea" placeholder="备注" autosize borderless />
|
|
</t-form-item>
|
|
</t-form>
|
|
<t-cell-group v-if="!isCreateMode" theme="card" v-entrance-transition="70">
|
|
<t-cell
|
|
title="营业日管理"
|
|
note="配置营业或休息日期"
|
|
arrow
|
|
@click="router.push(`/management/stores/${routeId}/business-days`)"
|
|
/>
|
|
<t-cell
|
|
title="自动休息日"
|
|
description="全天无任何登记记录时自动判定为休息日,休息日不视为中断连续登记事件"
|
|
>
|
|
<template #right-icon>
|
|
<t-switch v-model="formData.autoClosedDay" />
|
|
</template>
|
|
</t-cell>
|
|
<t-cell
|
|
title="直接产品消费事件"
|
|
:note="directProductSaleEventName"
|
|
description="直接产品订单使用该事件记录消费金额并触发积分"
|
|
arrow
|
|
@click="openDirectProductSaleEventPicker"
|
|
/>
|
|
</t-cell-group>
|
|
<div v-if="!isCreateMode && !isStoreManagerScope" class="actions" v-entrance-transition="140">
|
|
<t-button
|
|
v-can="permissionCodes.store.delete"
|
|
class="action"
|
|
theme="danger"
|
|
:loading="isRemoving"
|
|
@click="confirmRemoveStore"
|
|
>删除</t-button>
|
|
</div>
|
|
<store-remove-dialog
|
|
v-model:visible="removeDialogVisible"
|
|
:store="formData"
|
|
:loading="isRemoving"
|
|
@confirm="removeConfirmedStore"
|
|
/>
|
|
</div>
|
|
<t-popup v-model="directProductSaleEventPickerVisible" placement="bottom">
|
|
<t-picker
|
|
v-model="directProductSaleEventPickerDraft"
|
|
:columns="directProductSaleEventPickerColumns"
|
|
title="选择直接产品消费事件"
|
|
@cancel="directProductSaleEventPickerVisible = false"
|
|
@confirm="confirmDirectProductSaleEvent"
|
|
/>
|
|
</t-popup>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { useUserStore } from "timi-web-api";
|
|
import StoreAPI from "@/api/StoreAPI";
|
|
import type { EventItem } from "@/types/EventItem";
|
|
import StoreRemoveDialog from "@/components/StoreRemoveDialog.vue";
|
|
import { useCurrentStoreStore } from "@/store/currentStoreStore";
|
|
import { useNavBarStore } from "@/store/navBarStore";
|
|
import type { Store, StoreStatus } from "@/types/Store";
|
|
import { gaoRoleCodes, permissionCodes } from "@/utils/permission";
|
|
import { Message } from "@/utils/message.ts";
|
|
|
|
defineOptions({
|
|
name: "StoreEdit"
|
|
});
|
|
|
|
type PickerValue = string | number;
|
|
|
|
const route = useRoute();
|
|
const router = useRouter();
|
|
const navBarStore = useNavBarStore();
|
|
const userStore = useUserStore();
|
|
const currentStoreStore = useCurrentStoreStore();
|
|
const routeId = computed(() => String(route.params.id || ""));
|
|
const isStoreManagerScope = computed(() => userStore.hasRoles(gaoRoleCodes.storeManager) && !userStore.hasRoles(gaoRoleCodes.globalManager));
|
|
|
|
// ---------- 路由权限 ----------
|
|
|
|
const isCreateMode = computed(() => !routeId.value);
|
|
const savePermissionCode = computed(() => isCreateMode.value ? permissionCodes.store.create : permissionCodes.store.update);
|
|
|
|
// ---------- 表单 ----------
|
|
|
|
const isSaving = ref(false);
|
|
const isRemoving = ref(false);
|
|
const removeDialogVisible = ref(false);
|
|
|
|
const formData = reactive({
|
|
id: "",
|
|
code: "",
|
|
name: "",
|
|
status: "ACTIVE" as StoreStatus,
|
|
telephone: "",
|
|
address: "",
|
|
remark: "",
|
|
autoClosedDay: true,
|
|
directProductSaleEventId: ""
|
|
});
|
|
|
|
// ---------- 直接产品消费事件 ----------
|
|
|
|
const directProductSaleEventList = ref<EventItem[]>([]);
|
|
const directProductSaleEventLoaded = ref(false);
|
|
const directProductSaleEventPickerVisible = ref(false);
|
|
const directProductSaleEventPickerDraft = ref<PickerValue[]>([]);
|
|
const directProductSaleEventPickerColumns = computed(() => [
|
|
directProductSaleEventList.value
|
|
.map(item => ({
|
|
label: `${item.name || "未命名事件"}${item.requirePhoto ? "(需照片)" : ""}`,
|
|
value: item.id || ""
|
|
}))
|
|
.filter(item => item.value)
|
|
]);
|
|
const selectedDirectProductSaleEvent = computed(() => directProductSaleEventList.value.find(item => item.id === formData.directProductSaleEventId));
|
|
const directProductSaleEventName = computed(() => {
|
|
if (selectedDirectProductSaleEvent.value) {
|
|
return selectedDirectProductSaleEvent.value.name || "未命名事件";
|
|
}
|
|
if (formData.directProductSaleEventId && !directProductSaleEventLoaded.value) {
|
|
return "已配置,点击查看";
|
|
}
|
|
return formData.directProductSaleEventId ? "当前配置事件不可用" : "未配置,直接产品订单不可提交";
|
|
});
|
|
|
|
async function loadDirectProductSaleEvents(): Promise<boolean> {
|
|
if (!formData.id) {
|
|
return false;
|
|
}
|
|
try {
|
|
directProductSaleEventList.value = await StoreAPI.directProductSaleEventList(formData.id);
|
|
directProductSaleEventLoaded.value = true;
|
|
return true;
|
|
} catch {
|
|
directProductSaleEventList.value = [];
|
|
directProductSaleEventLoaded.value = false;
|
|
Message.error("直接产品消费事件加载失败");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async function openDirectProductSaleEventPicker(): Promise<void> {
|
|
if (!formData.id) {
|
|
Message.warning("请先保存门店,再配置直接产品消费事件");
|
|
return;
|
|
}
|
|
if (!await loadDirectProductSaleEvents()) {
|
|
return;
|
|
}
|
|
if (!directProductSaleEventPickerColumns.value[0]?.length) {
|
|
Message.warning("暂无可选事件,请先创建并启用 GENERAL / NUMBER / CENT 消费事件");
|
|
return;
|
|
}
|
|
directProductSaleEventPickerDraft.value = [formData.directProductSaleEventId];
|
|
directProductSaleEventPickerVisible.value = true;
|
|
}
|
|
|
|
function confirmDirectProductSaleEvent(value: Array<string | number>): void {
|
|
const eventId = String(value[0] || "");
|
|
if (eventId) {
|
|
formData.directProductSaleEventId = eventId;
|
|
}
|
|
directProductSaleEventPickerVisible.value = false;
|
|
}
|
|
|
|
async function doSave(): Promise<void> {
|
|
if (!validateForm()) {
|
|
return;
|
|
}
|
|
isSaving.value = true;
|
|
try {
|
|
if (isCreateMode.value) {
|
|
await StoreAPI.create(formData);
|
|
} else {
|
|
await StoreAPI.update(formData);
|
|
await refreshCurrentStore();
|
|
}
|
|
Message.success("门店已保存");
|
|
if (userStore.hasPermissions(permissionCodes.store.create)) {
|
|
await router.replace("/management/stores");
|
|
} else {
|
|
await router.replace("/store");
|
|
}
|
|
} finally {
|
|
isSaving.value = false;
|
|
}
|
|
}
|
|
|
|
async function refreshCurrentStore(): Promise<void> {
|
|
if (formData.id !== currentStoreStore.currentStoreId) {
|
|
return;
|
|
}
|
|
|
|
await currentStoreStore.refreshCurrentStore(formData.id);
|
|
}
|
|
|
|
function validateForm(): boolean {
|
|
if (!isStoreManagerScope.value && !formData.code) {
|
|
Message.warning("请填写门店编码");
|
|
return false;
|
|
}
|
|
if (!formData.name) {
|
|
Message.warning("请填写门店名称");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
onMounted(async () => {
|
|
if (userStore.hasPermissions(savePermissionCode.value)) {
|
|
navBarStore.setRightText("保存", "var(--td-brand-color)", doSave);
|
|
}
|
|
try {
|
|
if (!routeId.value) {
|
|
return;
|
|
}
|
|
const current = await StoreAPI.detail(routeId.value);
|
|
Object.assign(formData, current);
|
|
} catch {
|
|
Message.error("门店数据加载失败");
|
|
}
|
|
});
|
|
|
|
onUnmounted(() => navBarStore.clearRight());
|
|
|
|
// ---------- 删除 ----------
|
|
|
|
async function confirmRemoveStore(): Promise<void> {
|
|
if (!formData.id || isRemoving.value) {
|
|
return;
|
|
}
|
|
removeDialogVisible.value = true;
|
|
}
|
|
|
|
function closeRemoveDialog(): void {
|
|
removeDialogVisible.value = false;
|
|
}
|
|
|
|
async function removeConfirmedStore(store: Store): Promise<void> {
|
|
if (!store.id || isRemoving.value) {
|
|
return;
|
|
}
|
|
closeRemoveDialog();
|
|
await removeStore(store.id);
|
|
}
|
|
|
|
async function removeStore(id: string): Promise<void> {
|
|
if (isRemoving.value) {
|
|
return;
|
|
}
|
|
|
|
isRemoving.value = true;
|
|
try {
|
|
await StoreAPI.remove(id);
|
|
Message.success("门店已删除");
|
|
await router.replace("/management/stores");
|
|
} finally {
|
|
isRemoving.value = false;
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped lang="less">
|
|
.store-edit {
|
|
gap: 1rem;
|
|
padding: calc(var(--app-nav-height) + 1rem) 0 calc(1rem + env(safe-area-inset-bottom, 0)) 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
|
|
.form {
|
|
border-radius: .75rem;
|
|
}
|
|
|
|
.textarea {
|
|
width: 100%;
|
|
}
|
|
|
|
.links {
|
|
margin: 1rem 0;
|
|
}
|
|
|
|
.actions {
|
|
margin: 0 1rem;
|
|
display: flex;
|
|
|
|
.action {
|
|
flex: 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
</style>
|