disable form for bound customer code
This commit is contained in:
@@ -1,18 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="birthdate-picker">
|
<div class="birthdate-picker">
|
||||||
<t-radio-group class="mode" v-model="calendarProxy" variant="default-filled">
|
<t-radio-group class="mode" v-model="calendarProxy" variant="default-filled" :disabled="props.disabled">
|
||||||
<t-radio :block="false" borderless value="SOLAR">公历</t-radio>
|
<t-radio :block="false" borderless value="SOLAR">公历</t-radio>
|
||||||
<t-radio :block="false" borderless value="LUNAR">农历</t-radio>
|
<t-radio :block="false" borderless value="LUNAR">农历</t-radio>
|
||||||
<t-button
|
<t-button
|
||||||
v-if="modelValue"
|
v-if="modelValue"
|
||||||
class="clear"
|
class="clear"
|
||||||
size="extra-small"
|
size="extra-small"
|
||||||
|
:disabled="props.disabled"
|
||||||
@click="clearValue"
|
@click="clearValue"
|
||||||
>
|
>
|
||||||
清除
|
清除
|
||||||
</t-button>
|
</t-button>
|
||||||
</t-radio-group>
|
</t-radio-group>
|
||||||
<div class="value" @click="openPicker">
|
<div class="value" :class="{ disabled: props.disabled }" @click="openPicker">
|
||||||
<span :class="{ placeholder: !displayValue }" v-text="displayValue || placeholder" />
|
<span :class="{ placeholder: !displayValue }" v-text="displayValue || placeholder" />
|
||||||
<t-icon name="chevron-right" size="1.25rem" />
|
<t-icon name="chevron-right" size="1.25rem" />
|
||||||
</div>
|
</div>
|
||||||
@@ -58,10 +59,12 @@ type PickerValue = string | number;
|
|||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
modelValue?: string;
|
modelValue?: string;
|
||||||
calendar?: BirthdateCalendar;
|
calendar?: BirthdateCalendar;
|
||||||
|
disabled?: boolean;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
}>(), {
|
}>(), {
|
||||||
modelValue: "",
|
modelValue: "",
|
||||||
calendar: "SOLAR",
|
calendar: "SOLAR",
|
||||||
|
disabled: false,
|
||||||
placeholder: "请选择出生日期"
|
placeholder: "请选择出生日期"
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -145,6 +148,9 @@ const lunarColumns = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function openPicker(): void {
|
function openPicker(): void {
|
||||||
|
if (props.disabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (calendarProxy.value === "SOLAR") {
|
if (calendarProxy.value === "SOLAR") {
|
||||||
solarDraft.value = props.modelValue || today;
|
solarDraft.value = props.modelValue || today;
|
||||||
} else {
|
} else {
|
||||||
@@ -238,6 +244,11 @@ function clearValue(): void {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
color: var(--td-text-color-disabled);
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
span {
|
span {
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,12 @@
|
|||||||
:max-count="99"
|
:max-count="99"
|
||||||
:show-zero="false"
|
:show-zero="false"
|
||||||
>
|
>
|
||||||
<button class="nav-btn" type="button" @click="navBarStore.rightAction?.()">
|
<button
|
||||||
|
class="nav-btn"
|
||||||
|
type="button"
|
||||||
|
:disabled="navBarStore.rightDisabled"
|
||||||
|
@click="!navBarStore.rightDisabled && navBarStore.rightAction?.()"
|
||||||
|
>
|
||||||
<t-icon :name="navBarStore.rightIcon" size="1.25rem" />
|
<t-icon :name="navBarStore.rightIcon" size="1.25rem" />
|
||||||
</button>
|
</button>
|
||||||
</t-badge>
|
</t-badge>
|
||||||
@@ -31,7 +36,8 @@
|
|||||||
v-else-if="navBarStore.rightIcon"
|
v-else-if="navBarStore.rightIcon"
|
||||||
class="nav-btn"
|
class="nav-btn"
|
||||||
type="button"
|
type="button"
|
||||||
@click="navBarStore.rightAction?.()"
|
:disabled="navBarStore.rightDisabled"
|
||||||
|
@click="!navBarStore.rightDisabled && navBarStore.rightAction?.()"
|
||||||
>
|
>
|
||||||
<t-icon :name="navBarStore.rightIcon" size="1.25rem" />
|
<t-icon :name="navBarStore.rightIcon" size="1.25rem" />
|
||||||
</button>
|
</button>
|
||||||
@@ -39,9 +45,9 @@
|
|||||||
v-else
|
v-else
|
||||||
v-text="navBarStore.rightText"
|
v-text="navBarStore.rightText"
|
||||||
class="nav-text"
|
class="nav-text"
|
||||||
:style="{ color: navBarStore.rightTextColor ? navBarStore.rightTextColor : '#333' }"
|
:style="{ color: navBarStore.rightDisabled ? 'var(--td-text-color-disabled)' : navBarStore.rightTextColor ? navBarStore.rightTextColor : '#333' }"
|
||||||
:class="{ clickable: !!navBarStore.rightAction }"
|
:class="{ clickable: !!navBarStore.rightAction && !navBarStore.rightDisabled, disabled: navBarStore.rightDisabled }"
|
||||||
@click="navBarStore.rightAction?.()"
|
@click="!navBarStore.rightDisabled && navBarStore.rightAction?.()"
|
||||||
></span>
|
></span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -257,6 +263,11 @@ const tabBarHeight = computed(() => {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: var(--td-text-color-primary, #333);
|
color: var(--td-text-color-primary, #333);
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
color: var(--td-text-color-disabled);
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-title-slot {
|
.nav-title-slot {
|
||||||
@@ -288,6 +299,10 @@ const tabBarHeight = computed(() => {
|
|||||||
&.clickable {
|
&.clickable {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
v-model.trim="formData.code"
|
v-model.trim="formData.code"
|
||||||
placeholder="客户编码"
|
placeholder="客户编码"
|
||||||
borderless
|
borderless
|
||||||
|
@blur="handleCodeBlur"
|
||||||
>
|
>
|
||||||
<template #suffix-icon>
|
<template #suffix-icon>
|
||||||
<t-button
|
<t-button
|
||||||
@@ -22,22 +23,37 @@
|
|||||||
<code-scanner
|
<code-scanner
|
||||||
visible
|
visible
|
||||||
:status-text="scanStatus"
|
:status-text="scanStatus"
|
||||||
active-text="正在识别客户编码。"
|
active-text="正在识别客户编码"
|
||||||
@decode="handleDecodedText"
|
@decode="handleDecodedText"
|
||||||
@status-change="handleScanStatusChange"
|
@status-change="handleScanStatusChange"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="isCustomerBound" class="bound-customer">
|
||||||
|
<customer-profile-card :customer="boundCodeCustomer" />
|
||||||
|
<div class="tips red">
|
||||||
|
<t-icon name="error-circle" />
|
||||||
|
<span>该编码已绑定客户,不可新录入</span>
|
||||||
|
</div>
|
||||||
|
<t-button
|
||||||
|
block
|
||||||
|
theme="primary"
|
||||||
|
variant="outline"
|
||||||
|
@click="toBoundCustomerEdit"
|
||||||
|
>编辑该客户</t-button>
|
||||||
|
</div>
|
||||||
<t-form-item label="姓名" required-mark>
|
<t-form-item label="姓名" required-mark>
|
||||||
<t-input
|
<t-input
|
||||||
v-model.trim="formData.name"
|
v-model.trim="formData.name"
|
||||||
placeholder="姓名"
|
placeholder="姓名"
|
||||||
borderless
|
borderless
|
||||||
|
:disabled="isCustomerBound"
|
||||||
/>
|
/>
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="出生日期">
|
<t-form-item label="出生日期">
|
||||||
<birthdate-picker
|
<birthdate-picker
|
||||||
v-model="formData.birthdate"
|
v-model="formData.birthdate"
|
||||||
v-model:calendar="formData.birthdateCalendar"
|
v-model:calendar="formData.birthdateCalendar"
|
||||||
|
:disabled="isCustomerBound"
|
||||||
/>
|
/>
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="年龄">
|
<t-form-item label="年龄">
|
||||||
@@ -47,6 +63,7 @@
|
|||||||
:readonly="!!formData.birthdate"
|
:readonly="!!formData.birthdate"
|
||||||
:placeholder="formData.birthdate ? '根据出生日期自动计算' : '年龄'"
|
:placeholder="formData.birthdate ? '根据出生日期自动计算' : '年龄'"
|
||||||
borderless
|
borderless
|
||||||
|
:disabled="isCustomerBound"
|
||||||
/>
|
/>
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label-width="0">
|
<t-form-item label-width="0">
|
||||||
@@ -55,6 +72,9 @@
|
|||||||
v-model="photoList"
|
v-model="photoList"
|
||||||
v-model:photo-items="photoItemList"
|
v-model:photo-items="photoItemList"
|
||||||
:edit-mode="true"
|
:edit-mode="true"
|
||||||
|
:show-add="!isCustomerBound"
|
||||||
|
:show-remove="!isCustomerBound"
|
||||||
|
:show-viewer-actions="!isCustomerBound"
|
||||||
:add-actions="photoAddActions"
|
:add-actions="photoAddActions"
|
||||||
:loading="isPhotoUploading"
|
:loading="isPhotoUploading"
|
||||||
@add-photo="openPhotoPicker('PHOTO')"
|
@add-photo="openPhotoPicker('PHOTO')"
|
||||||
@@ -65,14 +85,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="性别">
|
<t-form-item label="性别">
|
||||||
<t-radio-group class="sex" v-model="formData.gender" variant="default-filled">
|
<t-radio-group class="sex" v-model="formData.gender" variant="default-filled" :disabled="isCustomerBound">
|
||||||
<t-radio :block="false" borderless value="MALE">男</t-radio>
|
<t-radio :block="false" borderless value="MALE">男</t-radio>
|
||||||
<t-radio :block="false" borderless value="FEMALE">女</t-radio>
|
<t-radio :block="false" borderless value="FEMALE">女</t-radio>
|
||||||
<t-radio :block="false" borderless value="">未知</t-radio>
|
<t-radio :block="false" borderless value="">未知</t-radio>
|
||||||
</t-radio-group>
|
</t-radio-group>
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="手机号">
|
<t-form-item label="手机号">
|
||||||
<t-input v-model.trim="formData.telephone" type="tel" placeholder="手机号" borderless />
|
<t-input v-model.trim="formData.telephone" type="tel" placeholder="手机号" borderless :disabled="isCustomerBound" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="介绍人">
|
<t-form-item label="介绍人">
|
||||||
<div class="referrer">
|
<div class="referrer">
|
||||||
@@ -81,15 +101,17 @@
|
|||||||
readonly
|
readonly
|
||||||
placeholder="选择或扫码"
|
placeholder="选择或扫码"
|
||||||
borderless
|
borderless
|
||||||
|
:disabled="isCustomerBound"
|
||||||
/>
|
/>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<t-button
|
<t-button
|
||||||
theme="primary"
|
theme="primary"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="extra-small"
|
size="extra-small"
|
||||||
|
:disabled="isCustomerBound"
|
||||||
@click="selectorVisible = true"
|
@click="selectorVisible = true"
|
||||||
>选择</t-button>
|
>选择</t-button>
|
||||||
<t-button theme="primary" size="extra-small" @click="toggleScanner('introducer')">
|
<t-button theme="primary" size="extra-small" :disabled="isCustomerBound" @click="toggleScanner('introducer')">
|
||||||
<span v-text="scannerVisible && scannerTarget === 'introducer' ? '关闭扫码' : '扫码'" />
|
<span v-text="scannerVisible && scannerTarget === 'introducer' ? '关闭扫码' : '扫码'" />
|
||||||
</t-button>
|
</t-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -104,14 +126,14 @@
|
|||||||
@status-change="handleScanStatusChange"
|
@status-change="handleScanStatusChange"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<t-form-item label="调理部位" arrow @click="openBodyAreaPicker">
|
<t-form-item label="调理部位" :arrow="!isCustomerBound" @click="openBodyAreaPicker">
|
||||||
<div class="picker-text" v-text="selectedAreaText" />
|
<div class="picker-text" v-text="selectedAreaText" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
</t-form>
|
</t-form>
|
||||||
<customer-tag-card
|
<customer-tag-card
|
||||||
v-if="canReadTag"
|
v-if="canReadTag"
|
||||||
class="tag-card"
|
class="tag-card"
|
||||||
:editable="canEditTag"
|
:editable="canEditTag && !isCustomerBound"
|
||||||
:tag-list="customerTagList"
|
:tag-list="customerTagList"
|
||||||
v-entrance-transition="70"
|
v-entrance-transition="70"
|
||||||
@edit="openTagSelector"
|
@edit="openTagSelector"
|
||||||
@@ -123,6 +145,7 @@
|
|||||||
v-model.trim="formData.address"
|
v-model.trim="formData.address"
|
||||||
:autosize="{ minRows: 3, maxRows: 5 }"
|
:autosize="{ minRows: 3, maxRows: 5 }"
|
||||||
placeholder="住址"
|
placeholder="住址"
|
||||||
|
:disabled="isCustomerBound"
|
||||||
/>
|
/>
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="病症">
|
<t-form-item label="病症">
|
||||||
@@ -131,6 +154,7 @@
|
|||||||
v-model.trim="formData.disease"
|
v-model.trim="formData.disease"
|
||||||
:autosize="{ minRows: 3, maxRows: 5 }"
|
:autosize="{ minRows: 3, maxRows: 5 }"
|
||||||
placeholder="病症"
|
placeholder="病症"
|
||||||
|
:disabled="isCustomerBound"
|
||||||
/>
|
/>
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="备注">
|
<t-form-item label="备注">
|
||||||
@@ -139,6 +163,7 @@
|
|||||||
v-model.trim="formData.remark"
|
v-model.trim="formData.remark"
|
||||||
:autosize="{ minRows: 3, maxRows: 5 }"
|
:autosize="{ minRows: 3, maxRows: 5 }"
|
||||||
placeholder="客户备注"
|
placeholder="客户备注"
|
||||||
|
:disabled="isCustomerBound"
|
||||||
/>
|
/>
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
</t-form>
|
</t-form>
|
||||||
@@ -150,12 +175,13 @@
|
|||||||
@change="handlePhotoSelected"
|
@change="handlePhotoSelected"
|
||||||
>
|
>
|
||||||
<customer-selector
|
<customer-selector
|
||||||
|
v-if="!isCustomerBound"
|
||||||
v-model="introducerCustomerId"
|
v-model="introducerCustomerId"
|
||||||
v-model:visible="selectorVisible"
|
v-model:visible="selectorVisible"
|
||||||
@change="handleIntroducerChange"
|
@change="handleIntroducerChange"
|
||||||
/>
|
/>
|
||||||
<tag-selector
|
<tag-selector
|
||||||
v-if="canEditTag"
|
v-if="canEditTag && !isCustomerBound"
|
||||||
v-model="selectedTagIdList"
|
v-model="selectedTagIdList"
|
||||||
v-model:visible="tagSelectorVisible"
|
v-model:visible="tagSelectorVisible"
|
||||||
@change="handleTagSelected"
|
@change="handleTagSelected"
|
||||||
@@ -168,7 +194,7 @@
|
|||||||
@left-button-click="cancelBodyAreaPicker"
|
@left-button-click="cancelBodyAreaPicker"
|
||||||
@right-button-click="confirmBodyAreaPicker"
|
@right-button-click="confirmBodyAreaPicker"
|
||||||
>
|
>
|
||||||
<body-area-selector v-model="bodyAreaDraft" />
|
<body-area-selector v-model="bodyAreaDraft" :readonly="isCustomerBound" />
|
||||||
</bottom-popup>
|
</bottom-popup>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -180,6 +206,7 @@ import CustomerAPI from "@/api/CustomerAPI";
|
|||||||
import TagAPI from "@/api/TagAPI";
|
import TagAPI from "@/api/TagAPI";
|
||||||
import BodyAreaSelector from "@/components/BodyAreaSelector.vue";
|
import BodyAreaSelector from "@/components/BodyAreaSelector.vue";
|
||||||
import CodeScanner from "@/components/CodeScanner.vue";
|
import CodeScanner from "@/components/CodeScanner.vue";
|
||||||
|
import CustomerProfileCard from "@/components/CustomerProfileCard.vue";
|
||||||
import CustomerSelector from "@/components/CustomerSelector.vue";
|
import CustomerSelector from "@/components/CustomerSelector.vue";
|
||||||
import PhotoScroller from "@/components/PhotoScroller.vue";
|
import PhotoScroller from "@/components/PhotoScroller.vue";
|
||||||
import TagSelector from "@/components/TagSelector.vue";
|
import TagSelector from "@/components/TagSelector.vue";
|
||||||
@@ -219,8 +246,17 @@ const scannerVisible = ref(false);
|
|||||||
const scannerTarget = ref<ScanTarget>("code");
|
const scannerTarget = ref<ScanTarget>("code");
|
||||||
const scanStatus = ref("点击扫码开始识别");
|
const scanStatus = ref("点击扫码开始识别");
|
||||||
const introducerCustomerId = ref("");
|
const introducerCustomerId = ref("");
|
||||||
|
const boundCodeCustomer = ref<Customer>();
|
||||||
|
const checkedCode = ref("");
|
||||||
|
let codeCheckToken = 0;
|
||||||
|
let pendingCodeCheck: Promise<void> | undefined;
|
||||||
|
let pendingCode = "";
|
||||||
|
const isCustomerBound = computed(() => isCreateMode.value && !!boundCodeCustomer.value?.id);
|
||||||
|
|
||||||
async function toggleScanner(target: ScanTarget): Promise<void> {
|
async function toggleScanner(target: ScanTarget): Promise<void> {
|
||||||
|
if (isCustomerBound.value && target !== "code") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (scannerVisible.value && scannerTarget.value === target) {
|
if (scannerVisible.value && scannerTarget.value === target) {
|
||||||
scannerVisible.value = false;
|
scannerVisible.value = false;
|
||||||
return;
|
return;
|
||||||
@@ -240,8 +276,9 @@ async function handleDecodedText(rawText: string): Promise<void> {
|
|||||||
if (scannerTarget.value === "code") {
|
if (scannerTarget.value === "code") {
|
||||||
// 选择客户扫码
|
// 选择客户扫码
|
||||||
formData.code = normalizedText;
|
formData.code = normalizedText;
|
||||||
scanStatus.value = `已识别客户编码 ${normalizedText}`;
|
|
||||||
scannerVisible.value = false;
|
scannerVisible.value = false;
|
||||||
|
await checkBoundCustomerCode();
|
||||||
|
scanStatus.value = `已识别客户编码 ${normalizedText}`;
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
// 选择介绍人扫码
|
// 选择介绍人扫码
|
||||||
@@ -253,6 +290,56 @@ function handleScanStatusChange(value: string): void {
|
|||||||
scanStatus.value = value;
|
scanStatus.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleCodeBlur(): Promise<void> {
|
||||||
|
if (!isCreateMode.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await checkBoundCustomerCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function checkBoundCustomerCode(): Promise<void> {
|
||||||
|
if (!isCreateMode.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedCode = formData.code?.trim() || "";
|
||||||
|
if (!normalizedCode) {
|
||||||
|
codeCheckToken++;
|
||||||
|
checkedCode.value = "";
|
||||||
|
boundCodeCustomer.value = undefined;
|
||||||
|
pendingCodeCheck = undefined;
|
||||||
|
pendingCode = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (checkedCode.value === normalizedCode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (pendingCodeCheck && pendingCode === normalizedCode) {
|
||||||
|
await pendingCodeCheck;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestToken = ++codeCheckToken;
|
||||||
|
boundCodeCustomer.value = undefined;
|
||||||
|
pendingCode = normalizedCode;
|
||||||
|
pendingCodeCheck = (async () => {
|
||||||
|
const matched = await CustomerAPI.findByCode(normalizedCode);
|
||||||
|
if (requestToken !== codeCheckToken || normalizedCode !== (formData.code?.trim() || "")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
checkedCode.value = normalizedCode;
|
||||||
|
boundCodeCustomer.value = matched?.id ? matched : undefined;
|
||||||
|
})();
|
||||||
|
try {
|
||||||
|
await pendingCodeCheck;
|
||||||
|
} finally {
|
||||||
|
if (pendingCode === normalizedCode) {
|
||||||
|
pendingCodeCheck = undefined;
|
||||||
|
pendingCode = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---------- 介绍人 ----------
|
// ---------- 介绍人 ----------
|
||||||
|
|
||||||
const selectorVisible = ref(false);
|
const selectorVisible = ref(false);
|
||||||
@@ -291,6 +378,18 @@ async function handleIntroducerChange(id: string): Promise<void> {
|
|||||||
const formData = reactive<Customer>({
|
const formData = reactive<Customer>({
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(() => formData.code, (value) => {
|
||||||
|
if (!isCreateMode.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const normalizedCode = value?.trim() || "";
|
||||||
|
if (normalizedCode !== checkedCode.value && normalizedCode !== pendingCode) {
|
||||||
|
codeCheckToken++;
|
||||||
|
checkedCode.value = "";
|
||||||
|
boundCodeCustomer.value = undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// ---------- 标签 ----------
|
// ---------- 标签 ----------
|
||||||
|
|
||||||
const canReadTag = computed(() => userStore.hasPermissions(corePermissionCodes.tag.read));
|
const canReadTag = computed(() => userStore.hasPermissions(corePermissionCodes.tag.read));
|
||||||
@@ -312,7 +411,7 @@ async function loadCustomerTags(customerId: string): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function openTagSelector(): void {
|
function openTagSelector(): void {
|
||||||
if (!canEditTag.value) {
|
if (!canEditTag.value || isCustomerBound.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
selectedTagIdList.value = customerTagList.value.map(item => item.id || "").filter(Boolean);
|
selectedTagIdList.value = customerTagList.value.map(item => item.id || "").filter(Boolean);
|
||||||
@@ -339,6 +438,9 @@ const selectedAreaText = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function openBodyAreaPicker(): void {
|
function openBodyAreaPicker(): void {
|
||||||
|
if (isCustomerBound.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
bodyAreaDraft.value = {
|
bodyAreaDraft.value = {
|
||||||
selectedCodeList: [...selectedAreas.value.selectedCodeList]
|
selectedCodeList: [...selectedAreas.value.selectedCodeList]
|
||||||
};
|
};
|
||||||
@@ -350,6 +452,10 @@ function cancelBodyAreaPicker(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function confirmBodyAreaPicker(): void {
|
function confirmBodyAreaPicker(): void {
|
||||||
|
if (isCustomerBound.value) {
|
||||||
|
bodyAreaPickerVisible.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
selectedAreas.value = {
|
selectedAreas.value = {
|
||||||
selectedCodeList: [...bodyAreaDraft.value.selectedCodeList]
|
selectedCodeList: [...bodyAreaDraft.value.selectedCodeList]
|
||||||
};
|
};
|
||||||
@@ -357,10 +463,24 @@ function confirmBodyAreaPicker(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function openPhotoPicker(target: CustomerAttachmentType): void {
|
function openPhotoPicker(target: CustomerAttachmentType): void {
|
||||||
|
if (isCustomerBound.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
photoPickerTarget.value = target;
|
photoPickerTarget.value = target;
|
||||||
photoInputRef.value?.click();
|
photoInputRef.value?.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch(isCustomerBound, (value) => {
|
||||||
|
navBarStore.setRightDisabled(value);
|
||||||
|
if (!value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scannerVisible.value = false;
|
||||||
|
selectorVisible.value = false;
|
||||||
|
tagSelectorVisible.value = false;
|
||||||
|
bodyAreaPickerVisible.value = false;
|
||||||
|
});
|
||||||
|
|
||||||
const canSubmit = computed(() => formData.name?.trim() && currentStoreStore.currentStoreId);
|
const canSubmit = computed(() => formData.name?.trim() && currentStoreStore.currentStoreId);
|
||||||
|
|
||||||
async function prepareStoreScope(): Promise<void> {
|
async function prepareStoreScope(): Promise<void> {
|
||||||
@@ -408,6 +528,7 @@ onMounted(() => {
|
|||||||
navBarStore.clearRight();
|
navBarStore.clearRight();
|
||||||
if (userStore.hasPermissions(savePermissionCode.value)) {
|
if (userStore.hasPermissions(savePermissionCode.value)) {
|
||||||
navBarStore.setRightText("保存", "var(--td-brand-color)", doSubmit);
|
navBarStore.setRightText("保存", "var(--td-brand-color)", doSubmit);
|
||||||
|
navBarStore.setRightDisabled(isCustomerBound.value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -526,6 +647,9 @@ async function doSubmit(): Promise<void> {
|
|||||||
if (isSubmitting.value) {
|
if (isSubmitting.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!await ensureCustomerCodeAvailable()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!canSubmit.value) {
|
if (!canSubmit.value) {
|
||||||
if (!formData.name?.trim()) {
|
if (!formData.name?.trim()) {
|
||||||
Message.warning("请填写姓名");
|
Message.warning("请填写姓名");
|
||||||
@@ -576,6 +700,26 @@ async function doSubmit(): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function ensureCustomerCodeAvailable(): Promise<boolean> {
|
||||||
|
if (!isCreateMode.value || !formData.code?.trim()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
await checkBoundCustomerCode();
|
||||||
|
if (!boundCodeCustomer.value?.id) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Message.warning("该客户编码已绑定客户,不可新录入,请编辑已有客户");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function toBoundCustomerEdit(): Promise<void> {
|
||||||
|
const customerId = boundCodeCustomer.value?.id;
|
||||||
|
if (!customerId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await router.push(`/management/customers/${customerId}/edit`);
|
||||||
|
}
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
navBarStore.clearRight();
|
navBarStore.clearRight();
|
||||||
});
|
});
|
||||||
@@ -616,6 +760,21 @@ function buildAttachmentList(): Attachment[] {
|
|||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bound-customer {
|
||||||
|
gap: .75rem;
|
||||||
|
padding: 1rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border-radius: 1rem;
|
||||||
|
|
||||||
|
.tips {
|
||||||
|
gap: .25rem;
|
||||||
|
display: flex;
|
||||||
|
font-size: .875rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.textarea {
|
.textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export const useNavBarStore = defineStore("nav-bar", () => {
|
|||||||
const rightBadge = ref<number>();
|
const rightBadge = ref<number>();
|
||||||
const rightText = ref("");
|
const rightText = ref("");
|
||||||
const rightTextColor = ref("");
|
const rightTextColor = ref("");
|
||||||
|
const rightDisabled = ref(false);
|
||||||
const rightAction = shallowRef<(() => void | Promise<void>) | undefined>();
|
const rightAction = shallowRef<(() => void | Promise<void>) | undefined>();
|
||||||
const customTitleVisible = ref(false);
|
const customTitleVisible = ref(false);
|
||||||
const customRightVisible = ref(false);
|
const customRightVisible = ref(false);
|
||||||
@@ -35,6 +36,7 @@ export const useNavBarStore = defineStore("nav-bar", () => {
|
|||||||
function setRightIcon(value?: string, action?: () => void | Promise<void>): void {
|
function setRightIcon(value?: string, action?: () => void | Promise<void>): void {
|
||||||
rightIcon.value = value || "";
|
rightIcon.value = value || "";
|
||||||
rightBadge.value = undefined;
|
rightBadge.value = undefined;
|
||||||
|
rightDisabled.value = false;
|
||||||
rightAction.value = action;
|
rightAction.value = action;
|
||||||
if (value) {
|
if (value) {
|
||||||
rightText.value = "";
|
rightText.value = "";
|
||||||
@@ -50,6 +52,7 @@ export const useNavBarStore = defineStore("nav-bar", () => {
|
|||||||
function setRightText(value?: string, color?: string, action?: () => void | Promise<void>): void {
|
function setRightText(value?: string, color?: string, action?: () => void | Promise<void>): void {
|
||||||
rightText.value = value || "";
|
rightText.value = value || "";
|
||||||
rightTextColor.value = color || "";
|
rightTextColor.value = color || "";
|
||||||
|
rightDisabled.value = false;
|
||||||
rightAction.value = action;
|
rightAction.value = action;
|
||||||
if (value) {
|
if (value) {
|
||||||
rightBadge.value = undefined;
|
rightBadge.value = undefined;
|
||||||
@@ -58,6 +61,10 @@ export const useNavBarStore = defineStore("nav-bar", () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setRightDisabled(value: boolean): void {
|
||||||
|
rightDisabled.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
function setCustomTitleVisible(value: boolean): void {
|
function setCustomTitleVisible(value: boolean): void {
|
||||||
customTitleVisible.value = value;
|
customTitleVisible.value = value;
|
||||||
}
|
}
|
||||||
@@ -69,6 +76,7 @@ export const useNavBarStore = defineStore("nav-bar", () => {
|
|||||||
rightBadge.value = undefined;
|
rightBadge.value = undefined;
|
||||||
rightText.value = "";
|
rightText.value = "";
|
||||||
rightTextColor.value = "";
|
rightTextColor.value = "";
|
||||||
|
rightDisabled.value = false;
|
||||||
rightAction.value = undefined;
|
rightAction.value = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -78,6 +86,7 @@ export const useNavBarStore = defineStore("nav-bar", () => {
|
|||||||
rightBadge.value = undefined;
|
rightBadge.value = undefined;
|
||||||
rightText.value = "";
|
rightText.value = "";
|
||||||
rightTextColor.value = "";
|
rightTextColor.value = "";
|
||||||
|
rightDisabled.value = false;
|
||||||
rightAction.value = undefined;
|
rightAction.value = undefined;
|
||||||
customRightVisible.value = false;
|
customRightVisible.value = false;
|
||||||
}
|
}
|
||||||
@@ -90,6 +99,7 @@ export const useNavBarStore = defineStore("nav-bar", () => {
|
|||||||
rightBadge,
|
rightBadge,
|
||||||
rightText,
|
rightText,
|
||||||
rightTextColor,
|
rightTextColor,
|
||||||
|
rightDisabled,
|
||||||
rightAction,
|
rightAction,
|
||||||
customTitleVisible,
|
customTitleVisible,
|
||||||
customRightVisible,
|
customRightVisible,
|
||||||
@@ -100,6 +110,7 @@ export const useNavBarStore = defineStore("nav-bar", () => {
|
|||||||
setRightIcon,
|
setRightIcon,
|
||||||
setRightBadge,
|
setRightBadge,
|
||||||
setRightText,
|
setRightText,
|
||||||
|
setRightDisabled,
|
||||||
setCustomTitleVisible,
|
setCustomTitleVisible,
|
||||||
setCustomRightVisible,
|
setCustomRightVisible,
|
||||||
clearRight
|
clearRight
|
||||||
|
|||||||
Reference in New Issue
Block a user