334 lines
8.0 KiB
Vue
334 lines
8.0 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-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>
|
||
<t-dialog
|
||
class="remove-dialog"
|
||
v-model:visible="removeDialogVisible"
|
||
title="删除门店"
|
||
>
|
||
<div class="content">
|
||
<div class="warning">删除后将同步处理以下数据:</div>
|
||
<ul class="items">
|
||
<li>删除该门店下的所有客户</li>
|
||
<li>删除客户的登记记录及相关附件</li>
|
||
<li>删除积分规则和积分账户</li>
|
||
<li>删除登记事件及事件角色关联</li>
|
||
<li>删除门店营业日数据</li>
|
||
<li>删除门店用户关系</li>
|
||
<li>删除门店自有标签</li>
|
||
</ul>
|
||
<div class="warning">特别说明:</div>
|
||
<ul class="items">
|
||
<li>已产生的积分流水不会删除,将保留积分撤销记录</li>
|
||
<li>关联的系统用户账号不会删除</li>
|
||
<li>删除后无法恢复,请谨慎操作</li>
|
||
</ul>
|
||
<div class="hint">
|
||
请输入门店名称
|
||
<span class="name red" v-text="removeStoreName" />
|
||
以确认删除
|
||
</div>
|
||
<t-input
|
||
class="confirm-input"
|
||
v-model.trim="removeConfirmName"
|
||
size="small"
|
||
borderless
|
||
clearable
|
||
placeholder="请输入门店名称"
|
||
/>
|
||
</div>
|
||
<template #cancelBtn>
|
||
<t-button
|
||
theme="default"
|
||
content="取消"
|
||
block
|
||
@click="closeRemoveDialog"
|
||
/>
|
||
</template>
|
||
<template #confirmBtn>
|
||
<t-button
|
||
theme="danger"
|
||
content="删除"
|
||
block
|
||
:disabled="!canConfirmRemove"
|
||
@click="removeConfirmedStore"
|
||
/>
|
||
</template>
|
||
</t-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { useUserStore } from "timi-web-api";
|
||
import StoreAPI from "@/api/StoreAPI";
|
||
import { useCurrentStoreStore } from "@/store/currentStoreStore";
|
||
import { useNavBarStore } from "@/store/navBarStore";
|
||
import type { StoreStatus } from "@/types/Store";
|
||
import { gaoRoleCodes, permissionCodes } from "@/utils/permission";
|
||
import { Message } from "@/utils/message.ts";
|
||
|
||
defineOptions({
|
||
name: "StoreEdit"
|
||
});
|
||
|
||
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 removeConfirmName = ref("");
|
||
|
||
const formData = reactive({
|
||
id: "",
|
||
code: "",
|
||
name: "",
|
||
status: "ACTIVE" as StoreStatus,
|
||
telephone: "",
|
||
address: "",
|
||
remark: "",
|
||
autoClosedDay: true
|
||
});
|
||
|
||
const removeStoreName = computed(() => formData.name);
|
||
const canConfirmRemove = computed(() => !!removeStoreName.value && removeConfirmName.value === removeStoreName.value);
|
||
|
||
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;
|
||
}
|
||
removeConfirmName.value = "";
|
||
removeDialogVisible.value = true;
|
||
}
|
||
|
||
function closeRemoveDialog(): void {
|
||
removeDialogVisible.value = false;
|
||
removeConfirmName.value = "";
|
||
}
|
||
|
||
async function removeConfirmedStore(): Promise<void> {
|
||
if (!formData.id || !canConfirmRemove.value || isRemoving.value) {
|
||
return;
|
||
}
|
||
closeRemoveDialog();
|
||
await removeStore();
|
||
}
|
||
|
||
async function removeStore(): Promise<void> {
|
||
if (isRemoving.value) {
|
||
return;
|
||
}
|
||
|
||
isRemoving.value = true;
|
||
try {
|
||
await StoreAPI.remove(formData.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;
|
||
}
|
||
}
|
||
}
|
||
|
||
.remove-dialog {
|
||
|
||
:deep(.t-dialog__footer) {
|
||
gap: 1rem;
|
||
}
|
||
|
||
.content {
|
||
text-align: left;
|
||
|
||
.warning {
|
||
color: #b42318;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.items {
|
||
margin: .25rem 0 1rem;
|
||
display: flex;
|
||
max-height: 5rem;
|
||
overflow-y: auto;
|
||
line-height: 1.2;
|
||
padding-left: 1.25rem;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.name {
|
||
font-weight: 700;
|
||
}
|
||
|
||
.confirm-input {
|
||
--td-input-vertical-padding: 10px;
|
||
|
||
border: 1px solid var(--td-component-border);
|
||
margin-top: .5rem;
|
||
border-radius: .75rem;
|
||
}
|
||
}
|
||
}
|
||
|
||
:global(html.theme-dark) {
|
||
.store-edit {
|
||
.remove-dialog {
|
||
|
||
.warning {
|
||
color: #fda29b;
|
||
}
|
||
|
||
.confirm-input {
|
||
background: rgba(15, 23, 42, .72);
|
||
border-color: rgba(148, 163, 184, .16);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|