提交
This commit is contained in:
1 parent
7b1d4c8729
commit
ffc89d9b57
75 files changed
+12263
-7950
No files matched your search
@@ -2,7 +2,7 @@
|
||||
<el-dialog
|
||||
title="选择客户"
|
||||
v-model="dialogVisible"
|
||||
width="1280px"
|
||||
width="1500px"
|
||||
align-center
|
||||
class="dialog-form-layout"
|
||||
modal-class="order-module-dialog-mask"
|
||||
@@ -39,6 +39,7 @@
|
||||
<div class="panel dialog-list-panel">
|
||||
<div class="list-body list-wrap dialog-list-body-only">
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
v-loading="loading"
|
||||
:data="customerList"
|
||||
class="list-table"
|
||||
@@ -48,7 +49,13 @@
|
||||
:max-height="400"
|
||||
>
|
||||
<el-table-column type="index" label="序号" min-width="60" align="center" header-align="center" />
|
||||
<el-table-column label="客户名称" prop="customerName" min-width="230" align="center" header-align="center" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="客户名称" prop="customerName" min-width="200" align="center" header-align="center" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="客户简称" prop="shortName" min-width="120" align="center" header-align="center" :show-overflow-tooltip="true">
|
||||
<template #default="scope">
|
||||
<span v-if="hasShortName(scope.row)">{{ scope.row.shortName }}</span>
|
||||
<el-tag v-else type="warning" size="small">未填写</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="客户类型" prop="customerType" min-width="150" align="center" header-align="center">
|
||||
<template #default="scope">
|
||||
<DictTag dict-type="crm_customer_type" :value="scope.row.customerType" />
|
||||
@@ -72,6 +79,13 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="负责人" prop="ownerUserName" min-width="100" align="center" header-align="center" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="操作" fixed="right" min-width="120" align="center" header-align="center" class-name="col-actions">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click.stop="handleEditShortName(scope.row)">
|
||||
<el-icon><Edit /></el-icon>{{ hasShortName(scope.row) ? '编辑简称' : '填写简称' }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<ListPagination
|
||||
@@ -90,6 +104,7 @@
|
||||
<div class="selected-customer">
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="客户名称">{{ currentCustomer.customerName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="客户简称">{{ currentCustomer.shortName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="客户类型">
|
||||
<DictTag dict-type="crm_customer_type" :value="currentCustomer.customerType" />
|
||||
</el-descriptions-item>
|
||||
@@ -110,7 +125,55 @@
|
||||
<el-button class="btn-ghost" @click="handleClose">
|
||||
<el-icon><Close /></el-icon>取消
|
||||
</el-button>
|
||||
<el-button class="btn-main" type="primary" @click="handleConfirm" :disabled="!currentCustomer">
|
||||
<el-button
|
||||
class="btn-main"
|
||||
type="primary"
|
||||
@click="handleConfirm"
|
||||
:disabled="!currentCustomer || !hasShortName(currentCustomer)"
|
||||
>
|
||||
<el-icon><Check /></el-icon>确定
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 填写/编辑客户简称 -->
|
||||
<el-dialog
|
||||
v-model="shortNameDialogVisible"
|
||||
:title="hasShortName(shortNameForm) ? '编辑客户简称' : '填写客户简称'"
|
||||
width="480px"
|
||||
align-center
|
||||
class="dialog-form-layout"
|
||||
modal-class="order-module-dialog-mask"
|
||||
:close-on-click-modal="false"
|
||||
append-to-body
|
||||
>
|
||||
<el-form
|
||||
ref="shortNameFormRef"
|
||||
:model="shortNameForm"
|
||||
:rules="shortNameRules"
|
||||
label-position="top"
|
||||
class="crm-dialog-form"
|
||||
>
|
||||
<el-form-item label="客户名称">
|
||||
<el-input :model-value="shortNameForm.customerName" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="客户简称" prop="shortName">
|
||||
<el-input
|
||||
v-model="shortNameForm.shortName"
|
||||
placeholder="请输入客户简称"
|
||||
maxlength="100"
|
||||
show-word-limit
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button class="btn-ghost" @click="shortNameDialogVisible = false">
|
||||
<el-icon><Close /></el-icon>取消
|
||||
</el-button>
|
||||
<el-button class="btn-main" type="primary" :loading="shortNameSaving" @click="handleSaveShortName">
|
||||
<el-icon><Check /></el-icon>确定
|
||||
</el-button>
|
||||
</div>
|
||||
@@ -122,10 +185,10 @@
|
||||
import { useDictStore } from '@/store/modules/dict'
|
||||
import DictTag from '@/components/DictTag/index.vue'
|
||||
import ListPagination from '@/components/ListPagination/index.vue'
|
||||
import { Check, Close, Refresh, Search } from '@element-plus/icons-vue'
|
||||
import { Check, Close, Edit, Refresh, Search } from '@element-plus/icons-vue'
|
||||
import { ref, watch } from 'vue'
|
||||
import type { ElFormInstance } from 'element-plus'
|
||||
import { fetchCustomerList } from '@/api/crm/customer'
|
||||
import type { ElFormInstance, FormItemRule } from 'element-plus'
|
||||
import { fetchCustomerList, fetchUpdateCustomer } from '@/api/crm/customer'
|
||||
|
||||
const dictStore = useDictStore()
|
||||
|
||||
@@ -167,6 +230,21 @@ const loading = ref(false)
|
||||
const total = ref(0)
|
||||
const currentCustomer = ref<any | null>(null)
|
||||
const queryFormRef = ref<ElFormInstance>()
|
||||
const tableRef = ref<any>()
|
||||
|
||||
const shortNameDialogVisible = ref(false)
|
||||
const shortNameSaving = ref(false)
|
||||
const shortNameFormRef = ref<ElFormInstance>()
|
||||
const shortNameForm = ref({
|
||||
id: '' as string | number,
|
||||
customerName: '',
|
||||
shortName: ''
|
||||
})
|
||||
const shortNameRules: Record<string, FormItemRule[]> = {
|
||||
shortName: [{ required: true, message: '客户简称不能为空', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
const hasShortName = (row?: { shortName?: string } | null) => Boolean(String(row?.shortName || '').trim())
|
||||
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
@@ -213,10 +291,66 @@ watchDebounced(
|
||||
)
|
||||
|
||||
const handleCurrentChange = (row: any) => {
|
||||
if (!row) {
|
||||
currentCustomer.value = null
|
||||
return
|
||||
}
|
||||
if (!hasShortName(row)) {
|
||||
ElMessage.warning('该客户尚未填写简称,请先填写客户简称后再选择')
|
||||
tableRef.value?.setCurrentRow(undefined)
|
||||
currentCustomer.value = null
|
||||
return
|
||||
}
|
||||
currentCustomer.value = row
|
||||
}
|
||||
|
||||
const handleEditShortName = (row: any) => {
|
||||
shortNameForm.value = {
|
||||
id: row.id,
|
||||
customerName: row.customerName || '',
|
||||
shortName: row.shortName || ''
|
||||
}
|
||||
shortNameDialogVisible.value = true
|
||||
nextTick(() => shortNameFormRef.value?.clearValidate())
|
||||
}
|
||||
|
||||
const handleSaveShortName = async () => {
|
||||
const valid = await shortNameFormRef.value?.validate().catch(() => false)
|
||||
if (!valid) return
|
||||
|
||||
shortNameSaving.value = true
|
||||
try {
|
||||
const trimmedShortName = shortNameForm.value.shortName.trim()
|
||||
await fetchUpdateCustomer({
|
||||
id: shortNameForm.value.id,
|
||||
shortName: trimmedShortName
|
||||
})
|
||||
ElMessage.success('客户简称保存成功')
|
||||
|
||||
const targetId = shortNameForm.value.id
|
||||
const updatedRow = customerList.value.find((item) => item.id === targetId)
|
||||
if (updatedRow) {
|
||||
updatedRow.shortName = trimmedShortName
|
||||
}
|
||||
if (currentCustomer.value?.id === targetId) {
|
||||
currentCustomer.value = { ...currentCustomer.value, shortName: trimmedShortName }
|
||||
}
|
||||
|
||||
shortNameDialogVisible.value = false
|
||||
} catch (error) {
|
||||
console.error('保存客户简称失败:', error)
|
||||
ElMessage.error('保存客户简称失败')
|
||||
} finally {
|
||||
shortNameSaving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleConfirm = () => {
|
||||
if (!currentCustomer.value) return
|
||||
if (!hasShortName(currentCustomer.value)) {
|
||||
ElMessage.warning('该客户尚未填写简称,请先填写客户简称后再选择')
|
||||
return
|
||||
}
|
||||
emit('confirm', currentCustomer.value)
|
||||
handleClose()
|
||||
}
|
||||
|
||||
@@ -54,6 +54,9 @@
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<span v-if="getCategoryLabel(file)" class="attachment-preview__category">
|
||||
{{ getCategoryLabel(file) }}
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
v-if="compact && !disabled"
|
||||
@@ -165,6 +168,8 @@ const props = withDefaults(
|
||||
disabled?: boolean
|
||||
/** 紧凑模式:小缩略图,只读时隐藏文件名与操作栏 */
|
||||
compact?: boolean
|
||||
/** 附件 URL → 分类名称,用于查看态在缩略图上展示分类标签 */
|
||||
categoryLabels?: Record<string, string>
|
||||
}>(),
|
||||
{
|
||||
modelValue: '',
|
||||
@@ -173,7 +178,8 @@ const props = withDefaults(
|
||||
accept: 'image/*,video/*,.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt,.zip,.rar',
|
||||
hint: '支持图片、视频、PDF、Word、Excel等文件',
|
||||
disabled: false,
|
||||
compact: false
|
||||
compact: false,
|
||||
categoryLabels: () => ({})
|
||||
}
|
||||
)
|
||||
|
||||
@@ -269,6 +275,11 @@ function getFileExtension(fileName: string) {
|
||||
return fileName.substring(lastDotIndex + 1).toUpperCase()
|
||||
}
|
||||
|
||||
function getCategoryLabel(file: AttachmentItem) {
|
||||
const label = props.categoryLabels?.[file.fileUrl]
|
||||
return label ? String(label).trim() : ''
|
||||
}
|
||||
|
||||
function canPreview(file: AttachmentItem) {
|
||||
return Boolean(file.fileUrl) && (isImageFile(file) || isVideoFile(file) || isPdfFile(file))
|
||||
}
|
||||
@@ -420,6 +431,7 @@ defineExpose({ hasUploading })
|
||||
}
|
||||
|
||||
.attachment-item {
|
||||
flex: 0 0 auto;
|
||||
width: 140px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
@@ -438,6 +450,7 @@ defineExpose({ hasUploading })
|
||||
}
|
||||
|
||||
.attachment-preview {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 96px;
|
||||
display: flex;
|
||||
@@ -490,6 +503,21 @@ defineExpose({ hasUploading })
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.attachment-preview__category {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
padding: 2px 4px;
|
||||
background: rgba(15, 23, 42, 0.62);
|
||||
color: #fff;
|
||||
font-size: 11px;
|
||||
line-height: 1.3;
|
||||
text-align: center;
|
||||
word-break: keep-all;
|
||||
}
|
||||
|
||||
.attachment-footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -565,7 +593,8 @@ defineExpose({ hasUploading })
|
||||
}
|
||||
|
||||
:deep(.el-upload) {
|
||||
display: block;
|
||||
display: inline-flex;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.attachment-upload--compact {
|
||||
|
||||
@@ -204,7 +204,7 @@
|
||||
调整运输时现场链接将轮换更新,旧现场链接将失效。
|
||||
</template>
|
||||
<template v-else-if="showDibangHint">
|
||||
勾选后将按产品进行地磅单生成(当月已有则复用),并在地磅出入库管理中产生对应记录。
|
||||
勾选后将按产品进行地磅单生成(当月已有则复用);入库单日期取送达时间,出库单日期取取货时间,未填时回退计划日期。
|
||||
</template>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<div class="view-kv-table-cell">
|
||||
<el-tag v-if="field.tagType" :type="field.tagType" size="small">{{
|
||||
<el-tag v-if="field.tagType" :type="field.tagType" size="small" class="view-kv-tag">{{
|
||||
formatViewValue(field.value)
|
||||
}}</el-tag>
|
||||
<ul v-else-if="field.items?.length" class="view-plain-list">
|
||||
<li v-for="(item, idx) in field.items" :key="idx">{{ item }}</li>
|
||||
</ul>
|
||||
<span v-else :class="{ 'money-display': field.money, 'muted-display': isMuted }">{{
|
||||
<span v-else :class="{ 'money-display': field.money, 'danger-display': field.danger, 'muted-display': isMuted }">{{
|
||||
formatViewValue(field.value)
|
||||
}}</span>
|
||||
<el-button
|
||||
@@ -49,7 +49,7 @@
|
||||
const onFieldView = inject<((field: ViewField) => void) | undefined>(VIEW_KV_FIELD_VIEW_KEY)
|
||||
|
||||
const isMuted = computed(
|
||||
() => !props.field.money && formatViewValue(props.field.value) === '—'
|
||||
() => !props.field.money && !props.field.danger && formatViewValue(props.field.value) === '—'
|
||||
)
|
||||
|
||||
const showViewBtn = computed(() => {
|
||||
@@ -91,6 +91,10 @@
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.view-kv-tag {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.view-kv-field-link {
|
||||
margin: 0;
|
||||
padding: 0 4px;
|
||||
@@ -112,4 +116,9 @@
|
||||
color: var(--text-muted, #64748b);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.danger-display {
|
||||
color: #dc2626;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
@@ -18,6 +18,8 @@ export interface ViewField {
|
||||
fullSpan?: boolean
|
||||
tagType?: TagType
|
||||
money?: boolean
|
||||
/** 红色加粗强调展示(如结算方式等关键业务字段) */
|
||||
danger?: boolean
|
||||
/** 值后展示「查看」按钮,点击时 ViewKvTable 抛出 field-view */
|
||||
viewTarget?: 'customer' | 'opportunity' | 'quote'
|
||||
viewId?: string | number | null
|
||||
|
||||
Reference in new issue
Block a user