|
@@ -1,11 +1,31 @@
|
|
|
<script setup lang="ts">
|
|
<script setup lang="ts">
|
|
|
import type { DataTableColumns } from 'naive-ui'
|
|
import type { DataTableColumns } from 'naive-ui'
|
|
|
-import { NDataTable, NFormItemGi, NInput, NPagination } from 'naive-ui'
|
|
|
|
|
|
|
+import {
|
|
|
|
|
+ NButton,
|
|
|
|
|
+ NDataTable,
|
|
|
|
|
+ NFormItem,
|
|
|
|
|
+ NFormItemGi,
|
|
|
|
|
+ NInput,
|
|
|
|
|
+ NModal,
|
|
|
|
|
+ NPagination,
|
|
|
|
|
+ NSelect,
|
|
|
|
|
+ NTag,
|
|
|
|
|
+ useMessage,
|
|
|
|
|
+} from 'naive-ui'
|
|
|
import AdminSearchPanel from '@/components/AdminSearchPanel.vue'
|
|
import AdminSearchPanel from '@/components/AdminSearchPanel.vue'
|
|
|
import AdminTablePageBar from '@/components/AdminTablePageBar.vue'
|
|
import AdminTablePageBar from '@/components/AdminTablePageBar.vue'
|
|
|
import * as customerApi from '@/api/modules/finance/customer'
|
|
import * as customerApi from '@/api/modules/finance/customer'
|
|
|
import type { CustomerItem, CustomerSearchParams } from '@/api/modules/finance/customer'
|
|
import type { CustomerItem, CustomerSearchParams } from '@/api/modules/finance/customer'
|
|
|
import { useTableColumnsControl } from '@/composables'
|
|
import { useTableColumnsControl } from '@/composables'
|
|
|
|
|
+import {
|
|
|
|
|
+ CUSTOMER_LEVEL_OPTIONS,
|
|
|
|
|
+ customerLevelTagType,
|
|
|
|
|
+ customerLevelText,
|
|
|
|
|
+ parseCustomerLevel,
|
|
|
|
|
+ type CustomerLevel,
|
|
|
|
|
+} from '@/utils/customerLevelLabel'
|
|
|
|
|
+
|
|
|
|
|
+const message = useMessage()
|
|
|
|
|
|
|
|
const loading = ref(false)
|
|
const loading = ref(false)
|
|
|
const list = ref<CustomerItem[]>([])
|
|
const list = ref<CustomerItem[]>([])
|
|
@@ -25,6 +45,11 @@ const search = ref<{
|
|
|
phone: '',
|
|
phone: '',
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
|
|
+const levelModalVisible = ref(false)
|
|
|
|
|
+const levelSubmitting = ref(false)
|
|
|
|
|
+const editingCustomer = ref<CustomerItem | null>(null)
|
|
|
|
|
+const editingLevel = ref<CustomerLevel | null>(null)
|
|
|
|
|
+
|
|
|
function amountText(v: unknown) {
|
|
function amountText(v: unknown) {
|
|
|
const n = Number(v ?? 0)
|
|
const n = Number(v ?? 0)
|
|
|
if (Number.isNaN(n)) return '0.00'
|
|
if (Number.isNaN(n)) return '0.00'
|
|
@@ -89,12 +114,56 @@ function resetSearch() {
|
|
|
void fetchList()
|
|
void fetchList()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+function openLevelEdit(row: CustomerItem) {
|
|
|
|
|
+ editingCustomer.value = row
|
|
|
|
|
+ editingLevel.value = parseCustomerLevel(row.levelLabel)
|
|
|
|
|
+ levelModalVisible.value = true
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+async function submitLevelEdit() {
|
|
|
|
|
+ const row = editingCustomer.value
|
|
|
|
|
+ if (!row?.id) {
|
|
|
|
|
+ message.warning('缺少客户 ID,无法修改')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ if (editingLevel.value == null) {
|
|
|
|
|
+ message.warning('请选择标签等级')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ levelSubmitting.value = true
|
|
|
|
|
+ try {
|
|
|
|
|
+ await customerApi.updateCustomerLevelLabel({
|
|
|
|
|
+ id: row.id,
|
|
|
|
|
+ levelLabel: editingLevel.value,
|
|
|
|
|
+ })
|
|
|
|
|
+ message.success('标签等级已更新')
|
|
|
|
|
+ levelModalVisible.value = false
|
|
|
|
|
+ await fetchList()
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ levelSubmitting.value = false
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
const columns = ref<DataTableColumns<CustomerItem>>([
|
|
const columns = ref<DataTableColumns<CustomerItem>>([
|
|
|
{ title: 'CID', key: 'cId', width: 120, ellipsis: { tooltip: true } },
|
|
{ title: 'CID', key: 'cId', width: 120, ellipsis: { tooltip: true } },
|
|
|
{ title: '邮箱', key: 'email', width: 220, ellipsis: { tooltip: true } },
|
|
{ title: '邮箱', key: 'email', width: 220, ellipsis: { tooltip: true } },
|
|
|
{ title: '姓名', key: 'name', width: 120, ellipsis: { tooltip: true } },
|
|
{ title: '姓名', key: 'name', width: 120, ellipsis: { tooltip: true } },
|
|
|
{ title: '手机号', key: 'phone', width: 140, ellipsis: { tooltip: true } },
|
|
{ title: '手机号', key: 'phone', width: 140, ellipsis: { tooltip: true } },
|
|
|
{ title: '身份证号', key: 'identity', width: 220, ellipsis: { tooltip: true } },
|
|
{ title: '身份证号', key: 'identity', width: 220, ellipsis: { tooltip: true } },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: '标签等级',
|
|
|
|
|
+ key: 'levelLabel',
|
|
|
|
|
+ width: 110,
|
|
|
|
|
+ render: (row) => {
|
|
|
|
|
+ const level = parseCustomerLevel(row.levelLabel)
|
|
|
|
|
+ if (!level) return '--'
|
|
|
|
|
+ return h(
|
|
|
|
|
+ NTag,
|
|
|
|
|
+ { size: 'small', type: customerLevelTagType(level), bordered: false },
|
|
|
|
|
+ { default: () => customerLevelText(level) },
|
|
|
|
|
+ )
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
{
|
|
{
|
|
|
title: '累计消费',
|
|
title: '累计消费',
|
|
|
key: 'totalSpendingAmount',
|
|
key: 'totalSpendingAmount',
|
|
@@ -107,6 +176,24 @@ const columns = ref<DataTableColumns<CustomerItem>>([
|
|
|
width: 180,
|
|
width: 180,
|
|
|
render: (row) => timeCell(row.addTime),
|
|
render: (row) => timeCell(row.addTime),
|
|
|
},
|
|
},
|
|
|
|
|
+ {
|
|
|
|
|
+ title: '操作',
|
|
|
|
|
+ key: 'actions',
|
|
|
|
|
+ width: 110,
|
|
|
|
|
+ fixed: 'right',
|
|
|
|
|
+ render(row) {
|
|
|
|
|
+ return h(
|
|
|
|
|
+ NButton,
|
|
|
|
|
+ {
|
|
|
|
|
+ size: 'small',
|
|
|
|
|
+ quaternary: true,
|
|
|
|
|
+ type: 'primary',
|
|
|
|
|
+ onClick: () => openLevelEdit(row),
|
|
|
|
|
+ },
|
|
|
|
|
+ { default: () => '修改等级' },
|
|
|
|
|
+ )
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
])
|
|
])
|
|
|
|
|
|
|
|
const { visibleKeys, displayColumns, columnOptions } = useTableColumnsControl(columns)
|
|
const { visibleKeys, displayColumns, columnOptions } = useTableColumnsControl(columns)
|
|
@@ -180,7 +267,7 @@ onActivated(() => {
|
|
|
:single-line="false"
|
|
:single-line="false"
|
|
|
:row-key="(row: CustomerItem) => `${row.id ?? ''}-${row.cId ?? ''}-${row.email ?? ''}-${row.phone ?? ''}`"
|
|
:row-key="(row: CustomerItem) => `${row.id ?? ''}-${row.cId ?? ''}-${row.email ?? ''}-${row.phone ?? ''}`"
|
|
|
class="data-table-fill"
|
|
class="data-table-fill"
|
|
|
- :scroll-x="1110"
|
|
|
|
|
|
|
+ :scroll-x="1340"
|
|
|
/>
|
|
/>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
@@ -198,5 +285,51 @@ onActivated(() => {
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</NCard>
|
|
</NCard>
|
|
|
|
|
+
|
|
|
|
|
+ <NModal
|
|
|
|
|
+ v-model:show="levelModalVisible"
|
|
|
|
|
+ preset="card"
|
|
|
|
|
+ title="修改标签等级"
|
|
|
|
|
+ style="width: min(420px, 92vw)"
|
|
|
|
|
+ :mask-closable="false"
|
|
|
|
|
+ @after-leave="() => { editingCustomer = null; editingLevel = null }"
|
|
|
|
|
+ >
|
|
|
|
|
+ <div v-if="editingCustomer" class="level-edit-meta">
|
|
|
|
|
+ <p><span class="level-edit-meta__label">CID:</span>{{ editingCustomer.cId ?? '--' }}</p>
|
|
|
|
|
+ <p><span class="level-edit-meta__label">姓名:</span>{{ editingCustomer.name ?? '--' }}</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <NFormItem label="标签等级" label-placement="top">
|
|
|
|
|
+ <NSelect
|
|
|
|
|
+ v-model:value="editingLevel"
|
|
|
|
|
+ :options="CUSTOMER_LEVEL_OPTIONS"
|
|
|
|
|
+ placeholder="请选择标签等级"
|
|
|
|
|
+ />
|
|
|
|
|
+ </NFormItem>
|
|
|
|
|
+ <template #footer>
|
|
|
|
|
+ <div class="level-edit-footer">
|
|
|
|
|
+ <NButton @click="levelModalVisible = false">取消</NButton>
|
|
|
|
|
+ <NButton type="primary" :loading="levelSubmitting" @click="submitLevelEdit">保存</NButton>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </NModal>
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
+
|
|
|
|
|
+<style scoped>
|
|
|
|
|
+.level-edit-meta {
|
|
|
|
|
+ margin-bottom: 12px;
|
|
|
|
|
+ color: var(--n-text-color-2);
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ line-height: 1.8;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.level-edit-meta__label {
|
|
|
|
|
+ color: var(--n-text-color-3);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.level-edit-footer {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: flex-end;
|
|
|
|
|
+ gap: 12px;
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|