This commit is contained in:
zlwu committed 2026-08-21 16:50:47 +08:00
1 parent 6dc8cfb2c1
commit d26fb23fcf
181 files changed
+25727 -24557

No files matched your search

+8 -10
View File
@@ -198,7 +198,6 @@
"196": "Community 196",
"197": "v2.4.2.1 - 2025-06-16",
"198": "fetchRoleOptions",
"199": "price-letter-template.ts",
"200": "index.js",
"201": "Ha",
"202": "Community 202",
@@ -235,29 +234,30 @@
"233": "Community 233",
"234": "Community 234",
"235": "index.js",
"236": "fetchData",
"236": "fastEnter.ts",
"237": "DbPoundSlipQrcodeDialog.vue",
"238": "pl-plan-contract-display.ts",
"239": "Community 239",
"240": "weighing.ts",
"240": "detail.vue",
"241": "index.vue",
"242": "Community 242",
"243": "Community 243",
"244": "expenseItemCode.ts",
"244": "loadUserOptions",
"245": "index.js",
"246": "handleSearch",
"246": "getPlMaintenanceApplyStatusLabel",
"247": "Community 247",
"248": "Community 248",
"249": "crm-product-display.ts",
"250": "handleAdd",
"251": "handleDisburse",
"252": "detail.vue",
"252": "index.js",
"253": "Community 253",
"254": "fetchDesignerData",
"255": "Community 255",
"256": "handleOpen",
"259": "Community 259",
"260": "Community 260",
"261": "Community 261",
"262": "form.vue",
"263": "index.vue",
"265": "Community 265",
"269": "Community 269",
"270": "Community 270",
@@ -335,7 +335,6 @@
"425": "Community 425",
"426": "Community 426",
"427": "Community 427",
"428": "Community 428",
"435": "index.vue",
"443": "index.vue",
"444": "index.vue",
@@ -504,7 +503,6 @@
"1200": "index.js",
"1201": "index.js",
"1202": "index.js",
"1203": "index.js",
"1204": "plugin.js",
"1205": "plugin.min.js",
"1206": "index.js",
@@ -138,6 +138,7 @@
"136": "Community 136",
"137": "Community 137",
"138": "FollowDialog.vue",
"139": "RenewDialog.vue",
"140": "Community 140",
"141": "Community 141",
"142": "Community 142",
@@ -145,6 +146,7 @@
"144": "Community 144",
"145": "api",
"146": "price-letter-template.ts",
"147": "index.vue",
"148": "Community 148",
"149": "fetchCustomerDetail",
"150": "Community 150",
@@ -233,27 +235,30 @@
"233": "Community 233",
"234": "Community 234",
"235": "index.js",
"236": "fetchData",
"236": "fastEnter.ts",
"237": "DbPoundSlipQrcodeDialog.vue",
"238": "pl-plan-contract-display.ts",
"239": "Community 239",
"240": "detail.vue",
"241": "index.vue",
"242": "Community 242",
"243": "Community 243",
"244": "loadUserOptions",
"245": "index.js",
"246": "getPlMaintenanceApplyStatusLabel",
"247": "Community 247",
"248": "Community 248",
"249": "crm-product-display.ts",
"250": "handleAdd",
"251": "handleDisburse",
"252": "detail.vue",
"252": "index.js",
"253": "Community 253",
"254": "fetchDesignerData",
"255": "Community 255",
"258": "getPlMaintenanceApplyStatusLabel",
"256": "handleOpen",
"259": "Community 259",
"260": "Community 260",
"261": "Community 261",
"262": "form.vue",
"263": "index.vue",
"265": "Community 265",
"269": "Community 269",
"270": "Community 270",
@@ -331,7 +336,6 @@
"425": "Community 425",
"426": "Community 426",
"427": "Community 427",
"428": "Community 428",
"435": "index.vue",
"443": "index.vue",
"444": "index.vue",
@@ -500,7 +504,6 @@
"1200": "index.js",
"1201": "index.js",
"1202": "index.js",
"1203": "index.js",
"1204": "plugin.js",
"1205": "plugin.min.js",
"1206": "index.js",
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+18
View File
@@ -291,6 +291,24 @@ export function fetchAllDictData() {
})
}
export function fetchEnsureDictData(data: {
dictType: string
label: string
preferredValue?: string
}) {
return request.post<{ label: string; value: string; created: boolean }>({
url: '/web/admin/dict-data/ensure',
data
})
}
export function fetchEnsureRemoveDictData(data: { dictType: string; value: string }) {
return request.post({
url: '/web/admin/dict-data/ensure-remove',
data
})
}
// ---------------------------------------------------------------------------
// 组织管理
// ---------------------------------------------------------------------------
@@ -0,0 +1,152 @@
<template>
<el-select
:model-value="modelValue"
:placeholder="placeholder"
:clearable="clearable"
:filterable="filterable"
:allow-create="allowCreate"
:default-first-option="allowCreate"
:disabled="disabled || loading"
:size="size"
:style="style"
@update:model-value="onSelect"
>
<el-option
v-for="opt in options"
:key="opt.value"
:label="opt.label"
:value="opt.value"
>
<div class="dict-select-option">
<span>{{ opt.label }}</span>
<el-button
v-if="dictStore.isSessionCreated(dictType, opt.value)"
link
type="danger"
size="small"
class="dict-select-option__remove"
@click.stop="onRemove(opt.value)"
>
<el-icon><Delete /></el-icon>
</el-button>
</div>
</el-option>
</el-select>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { Delete } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { useDictStore } from '@/store/modules/dict'
const props = withDefaults(
defineProps<{
dictType: string
modelValue?: string | null
allowCreate?: boolean
clearable?: boolean
filterable?: boolean
disabled?: boolean
placeholder?: string
size?: '' | 'default' | 'small' | 'large'
style?: string | Record<string, string>
}>(),
{
modelValue: '',
allowCreate: true,
clearable: true,
filterable: true,
disabled: false,
placeholder: '请选择',
size: 'default'
}
)
const emit = defineEmits<{
'update:modelValue': [value: string]
change: [value: string]
}>()
const dictStore = useDictStore()
const loading = ref(false)
const options = computed(() => dictStore.getOptions(props.dictType))
function isKnown(value: string) {
return options.value.some((o) => o.value === value || o.label === value)
}
async function onSelect(raw: string | null | undefined) {
const next = raw == null ? '' : String(raw).trim()
if (!next) {
emit('update:modelValue', '')
emit('change', '')
return
}
if (!props.allowCreate || isKnown(next)) {
const known = options.value.find((o) => o.value === next || o.label === next)
const value = known?.value ?? next
emit('update:modelValue', value)
emit('change', value)
return
}
try {
await ElMessageBox.confirm(`将「${next}」新增到字典,是否继续?`, '新增字典项', {
type: 'warning',
confirmButtonText: '确定',
cancelButtonText: '取消'
})
} catch {
return
}
loading.value = true
try {
const data = await dictStore.ensure(props.dictType, next)
emit('update:modelValue', data.value)
emit('change', data.value)
} catch (e: any) {
ElMessage.error(e?.message || '写入字典失败')
} finally {
loading.value = false
}
}
async function onRemove(value: string) {
try {
await ElMessageBox.confirm(`确定从字典中删除「${value}」吗?`, '删除字典项', {
type: 'warning',
confirmButtonText: '删除',
cancelButtonText: '取消'
})
} catch {
return
}
loading.value = true
try {
await dictStore.ensureRemove(props.dictType, value)
if (props.modelValue === value) {
emit('update:modelValue', '')
emit('change', '')
}
ElMessage.success('已删除')
} catch (e: any) {
ElMessage.error(e?.message || '删除失败')
} finally {
loading.value = false
}
}
</script>
<style scoped>
.dict-select-option {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.dict-select-option__remove {
flex-shrink: 0;
}
</style>
@@ -4,7 +4,7 @@
v-model:current-page="pageModel"
v-model:page-size="limitModel"
:total="total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="LIST_PAGE_SIZES"
layout="total, sizes, prev, pager, next, jumper"
@size-change="emitPagination"
@current-change="emitPagination"
@@ -14,7 +14,7 @@
<script setup lang="ts">
import { computed } from 'vue'
import { DEFAULT_LIST_PAGE_SIZE } from '@/utils/table/tableConfig'
import { DEFAULT_LIST_PAGE_SIZE, LIST_PAGE_SIZES } from '@/utils/table/tableConfig'
const props = defineProps<{
total: number
@@ -423,7 +423,7 @@
v-model:current-page="addPagination.page"
v-model:page-size="addPagination.pageSize"
:total="addPagination.total"
:page-sizes="[10, 20, 50]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="handleAddSizeChange"
@current-change="handleAddPageChange"
@@ -83,9 +83,20 @@
{{ row.model || row.productModel || '—' }}
</template>
</el-table-column>
<el-table-column label="单位" min-width="80" align="center">
<el-table-column label="单位" min-width="100" align="center">
<template #default="{ row }">
<DictTag dict-type="crm_product_unit" :value="row.unit" />
<DictSelect
v-if="editablePackageDelivery"
v-model="row.unit"
class="pl-plan-product-cell-control"
dict-type="crm_product_unit"
placeholder="请选择单位"
style="width: 100%"
/>
<template v-else>
<DictTag v-if="row.unit" dict-type="crm_product_unit" :value="row.unit" />
<span v-else>—</span>
</template>
</template>
</el-table-column>
<el-table-column label="计划数量" min-width="110" align="right">
@@ -126,25 +137,14 @@
</el-table-column>
<el-table-column prop="packageSpec" label="包装规格" min-width="150" align="center">
<template #default="{ row }">
<el-select
<DictSelect
v-if="editablePackageDelivery"
v-model="row.packageSpec"
class="pl-plan-product-cell-control"
dict-type="crm_product_gg"
placeholder="请选择包装规格"
filterable
allow-create
default-first-option
clearable
style="width: 100%"
@change="val => handlePackageSpecChange(row, val)"
>
<el-option
v-for="dict in dictStore.getOptions('crm_product_gg')"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
/>
<template v-else>
<DictTag v-if="row.packageSpec" dict-type="crm_product_gg" :value="row.packageSpec" />
<span v-else>—</span>
@@ -153,21 +153,14 @@
</el-table-column>
<el-table-column label="送货方式" min-width="150" align="center">
<template #default="{ row }">
<el-select
<DictSelect
v-if="editablePackageDelivery"
v-model="row.deliveryMethod"
class="pl-plan-product-cell-control"
dict-type="crm_shfs"
placeholder="请选择送货方式"
clearable
style="width: 100%"
>
<el-option
v-for="dict in dictStore.getOptions('crm_shfs')"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
/>
<template v-else>
<el-tag v-if="row.deliveryMethod" size="small" type="info">
{{ getDeliveryMethodDisplay(row.deliveryMethod) }}
@@ -236,6 +229,7 @@ import { formatBrandQtySummary } from '@/utils/pl-display'
import PlProductNameHazardCell from '@/components/business/pl-product-name-hazard-cell/index.vue'
import ProductAliasBrandDialog from '@/components/business/product-alias-brand-dialog/index.vue'
import DictTag from '@/components/DictTag/index.vue'
import DictSelect from '@/components/DictSelect/index.vue'
import { useDictStore } from '@/store/modules/dict'
const props = defineProps<{
@@ -248,7 +242,7 @@ const props = defineProps<{
editableQty?: boolean
/** 产品信息区:别名 / 品牌可改 */
editableAliasBrand?: boolean
/** 包装规格 / 送货方式可下拉编辑(对齐报价单) */
/** 单位 / 包装规格 / 送货方式可下拉编辑(对齐报价单) */
editablePackageDelivery?: boolean
/** 新增/编辑弹窗默认不展示实际数量 */
showActualQty?: boolean
@@ -269,9 +263,6 @@ const selectedProductIdSet = computed(
() => new Set((props.selectedProductIds || []).map(id => String(id)))
)
function handlePackageSpecChange(row: PlanFormProductRow, value: string | number | boolean | undefined) {
row.packageSpec = value == null || value === '' ? '' : String(value)
}
function getDeliveryMethodDisplay(method?: string) {
const label = dictStore.getLabel('crm_shfs', method || '')
@@ -80,7 +80,14 @@
</section>
<section class="pl-plan-form-section">
<h3 class="pl-plan-form-section-title">供需双方</h3>
<div class="pl-plan-form-section-head">
<h3 class="pl-plan-form-section-title">供需双方</h3>
<div class="pl-plan-form-section-actions">
<el-button class="btn-ghost" @click="handleSwapParties">
<el-icon><Sort /></el-icon>供需方互换
</el-button>
</div>
</div>
<el-form label-position="right" label-width="100px" class="pl-dialog-form">
<div class="pl-plan-party-row">
<div class="pl-plan-party-block">
@@ -377,7 +384,12 @@
</div>
<div class="pl-plan-logistics-block">
<h4 class="pl-plan-subtitle">送货信息</h4>
<div class="pl-plan-logistics-block-head">
<h4 class="pl-plan-subtitle">送货信息</h4>
<el-button text type="primary" @click="handleSwapPickupDelivery">
<el-icon><Sort /></el-icon>取送货互换
</el-button>
</div>
<div class="form-grid-2">
<el-form-item label="送货地址" class="form-col-span-2">
<PlAddressWithLibrary
@@ -472,7 +484,16 @@
</template>
<script setup lang="ts">
import { Check, Close, DocumentCopy, FolderOpened, Plus, Search, Van } from '@element-plus/icons-vue'
import {
Check,
Close,
DocumentCopy,
FolderOpened,
Plus,
Search,
Sort,
Van
} from '@element-plus/icons-vue'
import {
planApi,
transportApi,
@@ -838,6 +859,66 @@ function syncCommittedPartyIds() {
committedCustomerId.value = String(formData.customerId || '')
}
/** 供需方互换:仅交换本区字段,勿走 select change(会清空合同) */
function handleSwapParties() {
const temp = {
supplierId: formData.supplierId,
supplierName: formData.supplierName,
supplierContracts: [...formData.supplierContracts],
customerId: formData.customerId,
customerName: formData.customerName,
demanderContracts: [...formData.demanderContracts]
}
formData.supplierId = temp.customerId
formData.supplierName = temp.customerName
formData.supplierContracts = temp.demanderContracts
formData.customerId = temp.supplierId
formData.customerName = temp.supplierName
formData.demanderContracts = temp.supplierContracts
formData.destinationId = temp.supplierId
formData.destinationName = temp.supplierName
// 联系人仅绑定需方;互换后无供方联系人可对调,清空以免挂在错误客户上
formData.contactName = ''
formData.contactPhone = ''
syncCommittedPartyIds()
ElMessage.success('供需方已互换')
}
/** 取货信息 ↔ 送货信息 */
function handleSwapPickupDelivery() {
suppressAddressMutex.value = true
const temp = {
pickupAddress: formData.pickupAddress,
pickupLng: formData.pickupLng,
pickupLat: formData.pickupLat,
pickupTime: formData.pickupTime,
pickupContact: formData.pickupContact,
pickupNotes: formData.pickupNotes
}
formData.pickupAddress = formData.destinationAddress
formData.pickupLng = formData.destinationLng
formData.pickupLat = formData.destinationLat
formData.pickupTime = formData.deliveryTime
formData.pickupContact = formData.deliveryContact
formData.pickupNotes = formData.destinationNotes
formData.destinationAddress = temp.pickupAddress
formData.destinationLng = temp.pickupLng
formData.destinationLat = temp.pickupLat
formData.deliveryTime = temp.pickupTime
formData.deliveryContact = temp.pickupContact
formData.destinationNotes = temp.pickupNotes
nextTick(() => {
suppressAddressMutex.value = false
})
ElMessage.success('取送货信息已互换')
}
function handleSupplierSelectChange(customerId?: string) {
if (!customerId) {
formData.supplierId = ''
@@ -1711,6 +1792,17 @@ async function handleSubmit(mode: 'save' | 'saveAndArrange' = 'save') {
margin: 0 0 10px;
}
.pl-plan-logistics-block-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.pl-plan-logistics-block-head .pl-plan-subtitle {
margin: 0;
}
.pl-plan-party-select-row {
display: flex;
align-items: flex-start;
@@ -1838,7 +1930,11 @@ async function handleSubmit(mode: 'save' | 'saveAndArrange' = 'save') {
border-radius: 8px;
background: #f8fafc;
.pl-plan-subtitle {
> .pl-plan-subtitle {
margin: 0 0 8px;
}
.pl-plan-logistics-block-head {
margin: 0 0 8px;
}
@@ -17,7 +17,7 @@
<span class="selected-hint">已选 {{ selectedProducts.length }} 项</span>
<ListQuickSearchInput
v-model="quickSearchKeyword"
placeholder="产品名称/拼音首字母"
placeholder="产品名称/拼音首字母/规格型号"
@search="handleQuickSearch"
@clear="handleQuickSearchClear"
/>
@@ -317,6 +317,9 @@
page: queryParams.value.pageNum,
pageSize: queryParams.value.pageSize,
keyword: keyword || undefined,
// 选择器:名称/拼音/规格型号;不匹配产品编号(避免短数字误命中编号)
matchProductNo: false,
matchModel: true,
status: queryParams.value.status,
supplierId:
props.supplierId != null && props.supplierId !== '' ? String(props.supplierId) : undefined
@@ -470,7 +473,7 @@
margin-bottom: 12px;
:deep(.quick-search-input) {
width: 220px;
width: 280px;
flex-shrink: 0;
}
}
@@ -1,5 +1,6 @@
<script setup lang="ts">
import { computed } from 'vue'
import { LIST_PAGE_SIZES } from '@/utils/table/tableConfig'
import DataTablePanel from './DataTablePanel.vue'
import FilterBlock from './FilterBlock.vue'
import ListToolbar from './ListToolbar.vue'
@@ -23,7 +24,7 @@ const props = withDefaults(
tableScrollClass?: string
}>(),
{
pageSizes: () => [10, 20, 30],
pageSizes: () => [...LIST_PAGE_SIZES],
showSummary: false,
}
)
@@ -168,7 +168,7 @@
// 默认分页常量
const DEFAULT_PAGINATION_OPTIONS: PaginationOptions = {
pageSizes: [10, 20, 30, 50, 100],
pageSizes: [10, 20, 100, 500],
align: 'right',
background: true,
layout: layout.value,
@@ -122,8 +122,10 @@ export function usePlPlanArrangeActions() {
licensePlate?: string
driverUrl?: string
}) {
const lines = ['【运输安排】', `计划号:${plan.value?.planNo || '—'}`]
lines.push(`车牌:${options?.licensePlate || licensePlate.value || plan.value?.licensePlate || '—'}`)
const plate =
options?.licensePlate || licensePlate.value || plan.value?.licensePlate || '—'
const lines = [`【${plate} - 运输安排】`, `计划号:${plan.value?.planNo || '—'}`]
lines.push(`车牌:${plate}`)
lines.push(
`时间:${formatDate(plan.value?.pickupTime) || plan.value?.planDate || '—'}`
)
@@ -0,0 +1,12 @@
export const DICT_ENSURE_WHITELIST = [
'crm_product_gg',
'crm_shfs',
'crm_product_unit',
'crm_customer_park'
] as const
export type DictEnsureType = (typeof DICT_ENSURE_WHITELIST)[number]
export function isDictEnsureType(dictType: string): dictType is DictEnsureType {
return (DICT_ENSURE_WHITELIST as readonly string[]).includes(dictType)
}
+59 -2
View File
@@ -1,5 +1,10 @@
import { defineStore } from 'pinia'
import { fetchAllDictData } from '@/api/admin'
import {
fetchAllDictData,
fetchEnsureDictData,
fetchEnsureRemoveDictData
} from '@/api/admin'
import { isDictEnsureType } from '@/constants/dict-ensure-whitelist'
interface DictItem {
label: string
@@ -11,6 +16,8 @@ export const useDictStore = defineStore('dictStore', {
state: () => ({
dictMap: {} as Record<string, DictItem[]>,
loaded: false,
/** 本会话 ensure 新插入的 value,用于下拉显示删除按钮 */
sessionCreated: {} as Record<string, string[]>
}),
getters: {
@@ -29,6 +36,11 @@ export const useDictStore = defineStore('dictStore', {
getOptions: (state) => (dictType: string): DictItem[] => {
return state.dictMap[dictType] || []
},
isSessionCreated:
(state) =>
(dictType: string, value: string): boolean => {
return (state.sessionCreated[dictType] || []).includes(value)
}
},
actions: {
@@ -45,6 +57,51 @@ export const useDictStore = defineStore('dictStore', {
clearDict() {
this.dictMap = {}
this.loaded = false
this.sessionCreated = {}
},
},
upsertLocal(dictType: string, item: { label: string; value: string; tagType?: string }) {
const list = this.dictMap[dictType] ? [...this.dictMap[dictType]] : []
const idx = list.findIndex((i) => i.value === item.value)
const row: DictItem = {
label: item.label,
value: item.value,
tagType: item.tagType || 'info'
}
if (idx >= 0) list[idx] = row
else list.push(row)
this.dictMap[dictType] = list
},
removeLocal(dictType: string, value: string) {
const list = this.dictMap[dictType] || []
this.dictMap[dictType] = list.filter((i) => i.value !== value)
const created = this.sessionCreated[dictType] || []
this.sessionCreated[dictType] = created.filter((v) => v !== value)
},
markSessionCreated(dictType: string, value: string) {
const created = this.sessionCreated[dictType] ? [...this.sessionCreated[dictType]] : []
if (!created.includes(value)) created.push(value)
this.sessionCreated[dictType] = created
},
async ensure(dictType: string, label: string, preferredValue?: string) {
const text = (label || '').trim()
if (!text) throw new Error('字典标签不能为空')
if (!isDictEnsureType(dictType)) throw new Error('不支持的字典类型')
const res = await fetchEnsureDictData({
dictType,
label: text,
preferredValue
})
const data = (res as any)?.data || res
this.upsertLocal(dictType, { label: data.label, value: data.value })
if (data.created) this.markSessionCreated(dictType, data.value)
return data as { label: string; value: string; created: boolean }
},
async ensureRemove(dictType: string, value: string) {
const v = (value || '').trim()
if (!v) throw new Error('字典值不能为空')
if (!isDictEnsureType(dictType)) throw new Error('不支持的字典类型')
await fetchEnsureRemoveDictData({ dictType, value: v })
this.removeLocal(dictType, v)
}
}
})
+3
View File
@@ -37,6 +37,9 @@
/** 列表分页默认每页条数 */
export const DEFAULT_LIST_PAGE_SIZE = 20
/** 列表分页每页条数选项(全站统一) */
export const LIST_PAGE_SIZES: number[] = [10, 20, 100, 500]
export const tableConfig = {
// 响应数据字段映射配置,系统会从接口返回数据中按顺序查找这些字段
// 列表数据
@@ -252,7 +252,7 @@
v-model:page-size="pageSize"
:total="total"
layout="total, sizes, prev, pager, next"
:page-sizes="[10, 20, 30]"
:page-sizes="[10, 20, 100, 500]"
@size-change="onSizeChange"
/>
</div>
@@ -182,7 +182,7 @@
v-model:page-size="pendingPageSize"
:total="pendingTotal"
layout="total, sizes, prev, pager, next"
:page-sizes="[10, 20, 30]"
:page-sizes="[10, 20, 100, 500]"
@size-change="onPendingSizeChange"
/>
</div>
@@ -364,7 +364,7 @@
v-model:page-size="approvedPageSize"
:total="approvedTotal"
layout="total, sizes, prev, pager, next"
:page-sizes="[10, 20, 30]"
:page-sizes="[10, 20, 100, 500]"
@size-change="onApprovedSizeChange"
/>
</div>
@@ -201,7 +201,7 @@
v-model:page-size="pageSize"
:total="total"
layout="total, sizes, prev, pager, next"
:page-sizes="[10, 20, 30]"
:page-sizes="[10, 20, 100, 500]"
@size-change="onSizeChange"
/>
</div>
@@ -399,7 +399,7 @@
v-model:page-size="pageSize"
:total="total"
layout="total, sizes, prev, pager, next"
:page-sizes="[10, 20, 30]"
:page-sizes="[10, 20, 100, 500]"
@size-change="onSizeChange"
/>
</div>
@@ -93,7 +93,7 @@
v-model:current-page="page"
v-model:page-size="pageSize"
:total="total"
:page-sizes="[10, 20, 50]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
background
@size-change="handleSearch"
+3 -3
View File
@@ -194,7 +194,7 @@
v-model:page-size="pageSize"
:total="total"
layout="total, sizes, prev, pager, next"
:page-sizes="[10, 20, 30]"
:page-sizes="[10, 20, 100, 500]"
@size-change="onSizeChange"
/>
</div>
@@ -409,7 +409,7 @@
v-model:page-size="processedPageSize"
:total="processedTotal"
layout="total, sizes, prev, pager, next"
:page-sizes="[10, 20, 30]"
:page-sizes="[10, 20, 100, 500]"
@size-change="processedOnSizeChange"
/>
</div>
@@ -581,7 +581,7 @@
v-model:page-size="offsetAdvancePageSize"
:total="offsetAdvanceTotal"
layout="total, sizes, prev, pager, next"
:page-sizes="[5, 10, 20]"
:page-sizes="[10, 20, 100, 500]"
@current-change="fetchOffsetAdvances"
@size-change="handleOffsetAdvanceSizeChange"
/>
@@ -341,7 +341,7 @@
v-model:page-size="pageSize"
:total="total"
layout="total, sizes, prev, pager, next"
:page-sizes="[10, 20, 30, 50, 100, 200, 500]"
:page-sizes="[10, 20, 100, 500]"
@size-change="onSizeChange"
/>
</div>
@@ -231,7 +231,7 @@
v-model:page-size="pendingPageSize"
:total="pendingTotal"
layout="total, sizes, prev, pager, next"
:page-sizes="[10, 20, 30]"
:page-sizes="[10, 20, 100, 500]"
@size-change="onPendingSizeChange"
/>
</div>
@@ -462,7 +462,7 @@
v-model:page-size="approvedPageSize"
:total="approvedTotal"
layout="total, sizes, prev, pager, next"
:page-sizes="[10, 20, 30]"
:page-sizes="[10, 20, 100, 500]"
@size-change="onApprovedSizeChange"
/>
</div>
+1 -1
View File
@@ -227,7 +227,7 @@
v-model:page-size="pager.pageSize.value"
:total="pager.total.value"
layout="total, sizes, prev, pager, next"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
@size-change="pager.onSizeChange"
/>
</div>
+1 -1
View File
@@ -216,7 +216,7 @@
v-model:page-size="pageSize"
:total="filteredTotal"
layout="total, sizes, prev, pager, next"
:page-sizes="[10, 20, 30]"
:page-sizes="[10, 20, 100, 500]"
@size-change="onSizeChange"
/>
</div>
+1 -1
View File
@@ -231,7 +231,7 @@
v-model:page-size="pageSize"
:total="filteredTotal"
layout="total, sizes, prev, pager, next"
:page-sizes="[10, 20, 30]"
:page-sizes="[10, 20, 100, 500]"
@size-change="onSizeChange"
/>
</div>
@@ -117,7 +117,7 @@
v-model:current-page="followQuery.pageNum"
v-model:page-size="followQuery.pageSize"
:total="followTotal"
:page-sizes="[10, 20, 50]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="getFollowList"
@current-change="getFollowList"
+6 -47
View File
@@ -84,21 +84,11 @@
</el-select>
</el-form-item>
<el-form-item label="所在园区" prop="parkArea">
<el-select
<DictSelect
v-model="form.parkArea"
dict-type="crm_customer_park"
placeholder="请选择或输入所在园区"
clearable
filterable
allow-create
default-first-option
>
<el-option
v-for="opt in parkAreaOptions"
:key="opt.value"
:label="opt.label"
:value="opt.value"
/>
</el-select>
/>
</el-form-item>
<el-form-item label="省份" prop="province">
<el-input v-model="form.province" placeholder="请输入省份" maxlength="50" />
@@ -176,10 +166,11 @@
<script setup lang="ts">
import { Close, Check } from '@element-plus/icons-vue'
import { getClue, addClue, updateClue, checkDuplicateClue, getParkAreaList } from '@/api/crm/clue'
import { getClue, addClue, updateClue, checkDuplicateClue } from '@/api/crm/clue'
import type { ClueForm as ClueFormType } from '@/api/crm/clue/types'
import { fetchUserOptions } from '@/api/admin'
import { useDictStore } from '@/store/modules/dict'
import DictSelect from '@/components/DictSelect/index.vue'
import { useUserStore } from '@/store/modules/user'
const dictStore = useDictStore()
@@ -224,27 +215,6 @@
})
const userList = ref<any[]>([])
const extraParkAreas = ref<string[]>([])
const parkAreaOptions = computed(() => {
const dictOpts = dictStore.getOptions('crm_customer_park')
const seen = new Set(dictOpts.map((opt) => opt.value))
const options = [...dictOpts]
for (const area of extraParkAreas.value) {
if (area && !seen.has(area)) {
options.push({ label: area, value: area, tagType: 'info' })
seen.add(area)
}
}
const current = form.value.parkArea?.trim()
if (current && !seen.has(current)) {
options.push({ label: current, value: current, tagType: 'info' })
}
return options
})
const dialog = reactive({ visible: false, title: '' })
const submitting = ref(false)
@@ -266,7 +236,7 @@
const open = async (id?: string | number) => {
reset()
dialog.visible = true
await Promise.all([loadUserList(), loadParkAreaList()])
await loadUserList()
if (id) {
dialog.title = '修改线索'
@@ -301,16 +271,6 @@
}
}
const loadParkAreaList = async () => {
try {
const res = await getParkAreaList()
const list = (res as any)?.data ?? res ?? []
extraParkAreas.value = Array.isArray(list) ? list.filter(Boolean) : []
} catch (error) {
console.error(error)
extraParkAreas.value = []
}
}
const submitForm = () => {
formRef.value?.validate(async (valid: boolean) => {
@@ -385,7 +345,6 @@
remark: undefined
}
submitting.value = false
extraParkAreas.value = []
formRef.value?.resetFields()
}
+1 -1
View File
@@ -402,7 +402,7 @@
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next, jumper"
background
@size-change="getList"
@@ -478,7 +478,7 @@
v-model:current-page="queryParams.pageNum"
v-model:page-size="queryParams.pageSize"
:total="total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next, jumper"
@size-change="getList"
@current-change="getList"
@@ -173,7 +173,7 @@
<el-pagination
v-model:current-page="queryParams.pageNum"
v-model:page-size="queryParams.pageSize"
:page-sizes="[10, 20, 30, 50]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="getList"
@@ -241,7 +241,7 @@
<el-pagination
v-model:current-page="queryParams.pageNum"
v-model:page-size="queryParams.pageSize"
:page-sizes="[10, 20, 30, 50]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="getList"
@@ -426,16 +426,13 @@
</el-table-column>
<el-table-column label="单位" prop="unit" width="100" align="center">
<template #default="scope">
<el-select
<DictSelect
v-model="scope.row.unit"
dict-type="crm_product_unit"
placeholder="请选择单位"
clearable
size="small"
style="width: 100%"
@change="handleUnitChange(scope.row)"
>
<el-option v-for="dict in dictStore.getOptions('crm_product_unit')" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
/>
</template>
</el-table-column>
<el-table-column prop="quantity" width="130">
@@ -574,27 +571,25 @@
<el-table-column prop="packageSpec" min-width="150">
<template #header> <span style="color: #f56c6c">*</span> 包装规格</template>
<template #default="scope">
<el-select
<DictSelect
v-model="scope.row.packageSpec"
dict-type="crm_product_gg"
placeholder="请选择包装规格"
filterable
allow-create
default-first-option
clearable
size="small"
style="width: 100%"
@change="handlePackageSpecChange(scope.row, $event)"
>
<el-option v-for="dict in dictStore.getOptions('crm_product_gg')" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
/>
</template>
</el-table-column>
<el-table-column prop="deliveryMethod" min-width="150">
<template #header> <span style="color: #f56c6c">*</span> 送货方式</template>
<template #default="scope">
<el-select v-model="scope.row.deliveryMethod" placeholder="请选择送货方式" clearable size="small" style="width: 100%">
<el-option v-for="dict in dictStore.getOptions('crm_shfs')" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
<DictSelect
v-model="scope.row.deliveryMethod"
dict-type="crm_shfs"
placeholder="请选择送货方式"
size="small"
style="width: 100%"
/>
</template>
</el-table-column>
<el-table-column prop="remark" label="备注" min-width="160">
@@ -831,7 +826,7 @@ import {
type ContractProductForm,
type ContractProductPriceTierForm
} from '@/api/crm/contract/index'
import { isFrameworkContractType } from '../contractType'
import { isFrameworkContractType, isPurchaseLikeContractType } from '../contractType'
import {
FRAMEWORK_QUANTITY_LABEL,
FRAMEWORK_PRICE_LABEL,
@@ -879,6 +874,7 @@ import { getOpportunity } from '@/api/crm/opportunity'
import { getQuote } from '@/api/crm/quote'
import ProductSelector from '@/components/business/product-selector/index.vue'
import ProductAliasBrandDialog from '@/components/business/product-alias-brand-dialog/index.vue'
import DictSelect from '@/components/DictSelect/index.vue'
import CustomerSelector from '@/components/CustomerSelector.vue'
import ContactSelector from '@/components/ContactSelector.vue'
import ContactFormDialog from '@/views/crm/customer/components/ContactFormDialog.vue'
@@ -2268,9 +2264,9 @@ const handleContractTypeChange = (value: string) => {
})
calculateTotalAmount()
}
if (value === 'purchase' && !isPartiesSwapped.value) {
if (isPurchaseLikeContractType(value) && !isPartiesSwapped.value) {
$modal
.confirm('检测到您选择了采购合同,系统将自动调换供需方信息,是否继续?')
.confirm('检测到您选择了采购类合同,系统将自动调换供需方信息,是否继续?')
.then(async () => {
await handleSwapParties()
})
@@ -2502,16 +2498,6 @@ const handleTaxRateChange = (row: ExtendedContractProductForm) => {
calculateTotalAmount()
}
/** 单位变化 */
const handleUnitChange = (_row: ExtendedContractProductForm) => {
// 单位变化通常不需要重新计算金额
}
/** 包装规格变更处理 */
const handlePackageSpecChange = (product: ExtendedContractProductForm, value: string) => {
product.packageSpec = value
}
const handleEnableFrameworkQuantityFixed = (row: ExtendedContractProductForm) => {
enableFrameworkQuantityFixed(row)
calculateProductAmount(row)
@@ -11,6 +11,15 @@ export function isFrameworkContractType(type?: string | null): boolean {
return FRAMEWORK_CONTRACT_TYPES.includes((type || '').trim() as FrameworkContractType)
}
/** 采购类合同(含框架采购):选择后需提示并调换供需方 */
export const PURCHASE_LIKE_CONTRACT_TYPES = ['purchase', 'framework_purchase'] as const
export type PurchaseLikeContractType = (typeof PURCHASE_LIKE_CONTRACT_TYPES)[number]
export function isPurchaseLikeContractType(type?: string | null): boolean {
return PURCHASE_LIKE_CONTRACT_TYPES.includes((type || '').trim() as PurchaseLikeContractType)
}
/** 允许打开编辑的合同状态(保存后回草稿) */
export const CONTRACT_EDITABLE_STATUSES = ['draft', 'approving', 'approved'] as const
+18 -33
View File
@@ -292,16 +292,13 @@
</el-table-column>
<el-table-column label="单位" prop="unit" width="100" align="center">
<template #default="scope">
<el-select
<DictSelect
v-model="scope.row.unit"
dict-type="crm_product_unit"
placeholder="请选择单位"
clearable
size="small"
style="width: 100%"
@change="handleUnitChange(scope.row)"
>
<el-option v-for="dict in dictStore.getOptions('crm_product_unit')" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
/>
</template>
</el-table-column>
<el-table-column prop="quantity" width="130">
@@ -370,27 +367,25 @@
<el-table-column prop="packageSpec" min-width="150">
<template #header> <span style="color: #f56c6c">*</span> 包装规格</template>
<template #default="scope">
<el-select
<DictSelect
v-model="scope.row.packageSpec"
dict-type="crm_product_gg"
placeholder="请选择包装规格"
filterable
allow-create
default-first-option
clearable
size="small"
style="width: 100%"
@change="(val) => handlePackageSpecChange(scope.row, val)"
>
<el-option v-for="dict in dictStore.getOptions('crm_product_gg')" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
/>
</template>
</el-table-column>
<el-table-column prop="deliveryMethod" min-width="150">
<template #header> <span style="color: #f56c6c">*</span> 送货方式</template>
<template #default="scope">
<el-select v-model="scope.row.deliveryMethod" placeholder="请选择送货方式" clearable size="small" style="width: 100%">
<el-option v-for="dict in dictStore.getOptions('crm_shfs')" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
<DictSelect
v-model="scope.row.deliveryMethod"
dict-type="crm_shfs"
placeholder="请选择送货方式"
size="small"
style="width: 100%"
/>
</template>
</el-table-column>
<el-table-column prop="remark" label="备注" min-width="160">
@@ -546,7 +541,7 @@ import {
type ContractProductForm,
type ContractProductPriceTierForm
} from '@/api/crm/contract/index';
import { isFrameworkContractType } from './contractType';
import { isFrameworkContractType, isPurchaseLikeContractType } from './contractType';
import {
FRAMEWORK_QUANTITY_LABEL,
FRAMEWORK_PRICE_LABEL,
@@ -579,6 +574,7 @@ import AttachmentUploadField from '@/components/business/attachment-upload-field
import { getOpportunity } from '@/api/crm/opportunity';
import { getQuote } from '@/api/crm/quote';
import ProductSelector from '@/components/business/product-selector/index.vue';
import DictSelect from '@/components/DictSelect/index.vue'
import CustomerSelector from '@/components/CustomerSelector.vue';
import ContactSelector from '@/components/ContactSelector.vue';
import QuoteSelector from '@/components/QuoteSelector.vue';
@@ -1409,10 +1405,10 @@ const handleContractTypeChange = (value: string) => {
});
calculateTotalAmount();
}
// 当选择采购合同时,自动调换供需方
if (value === 'purchase' && !isPartiesSwapped.value) {
// 当选择采购类合同时,自动调换供需方
if (isPurchaseLikeContractType(value) && !isPartiesSwapped.value) {
$modal
.confirm('检测到您选择了采购合同,系统将自动调换供需方信息,是否继续?')
.confirm('检测到您选择了采购类合同,系统将自动调换供需方信息,是否继续?')
.then(() => {
handleSwapParties();
})
@@ -1596,17 +1592,6 @@ const handleTaxRateChange = (row: ExtendedContractProductForm) => {
calculateTotalAmount();
};
/** 单位变化 */
const handleUnitChange = (row: ExtendedContractProductForm) => {
// 单位变化通常不需要重新计算金额,但可以在这里添加其他逻辑
// 例如:记录变更日志、验证单位有效性等
};
/** 包装规格变更处理(支持自由输入并写入字典) */
const handlePackageSpecChange = (product: ExtendedContractProductForm, value: string) => {
product.packageSpec = value;
};
const handleEnableFrameworkQuantityFixed = (row: ExtendedContractProductForm) => {
enableFrameworkQuantityFixed(row);
calculateProductAmount(row);
@@ -186,7 +186,7 @@
<el-pagination
v-model:current-page="queryParams.pageNum"
v-model:page-size="queryParams.pageSize"
:page-sizes="[10, 20, 30, 50]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="getList"
@@ -100,7 +100,7 @@
v-model:current-page="followQuery.pageNum"
v-model:page-size="followQuery.pageSize"
:total="followTotal"
:page-sizes="[10, 20, 50]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="getFollowList"
@current-change="getFollowList"
@@ -430,7 +430,7 @@
v-model:current-page="logQuery.pageNum"
v-model:page-size="logQuery.pageSize"
:total="logTotal"
:page-sizes="[10, 20, 50]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="() => getOperLogList(false)"
@current-change="() => getOperLogList(false)"
@@ -104,21 +104,11 @@
</el-select>
</el-form-item>
<el-form-item label="所在园区" prop="parkArea">
<el-select
<DictSelect
v-model="form.parkArea"
dict-type="crm_customer_park"
placeholder="请选择或输入所在园区"
clearable
filterable
allow-create
default-first-option
>
<el-option
v-for="opt in parkAreaOptions"
:key="opt.value"
:label="opt.label"
:value="opt.value"
/>
</el-select>
/>
</el-form-item>
<el-form-item label="企业规模" prop="scale">
<el-select v-model="form.scale" placeholder="请选择企业规模" clearable>
@@ -323,15 +313,14 @@
</template>
<script setup lang="ts">
import { ref, reactive, computed, nextTick } from 'vue'
import { ref, reactive, nextTick } from 'vue'
import { Close, Check, Refresh, DocumentCopy, Delete } from '@element-plus/icons-vue'
import type { FormItemRule } from 'element-plus'
import { ElMessage } from 'element-plus'
import {
fetchCustomerDetail,
fetchCreateCustomer,
fetchUpdateCustomer,
fetchParkAreaList
fetchUpdateCustomer
} from '@/api/crm/customer'
import type { CustomerForm as CustomerFormType } from '@/api/crm/customerTypes'
import { addInvoiceInfo } from '@/api/crm/invoiceInfo'
@@ -341,6 +330,7 @@ import {
mergeInvoiceRemark
} from '@/utils/crm-invoice-paste-recognize'
import { useDictStore } from '@/store/modules/dict'
import DictSelect from '@/components/DictSelect/index.vue'
import { useUserStore } from '@/store/modules/user'
import { fetchUserOptions } from '@/api/admin'
import AttachmentUploadField from '@/components/business/attachment-upload-field/index.vue'
@@ -402,7 +392,6 @@ const rules = ref<Record<string, FormItemRule[]>>({
// 用户列表
const userList = ref<any[]>([])
const extraParkAreas = ref<string[]>([])
const invoiceForm = ref<InvoiceInfoForm>({
companyName: '',
@@ -416,25 +405,6 @@ const invoiceForm = ref<InvoiceInfoForm>({
remark: ''
})
const parkAreaOptions = computed(() => {
const dictOpts = dictStore.getOptions('crm_customer_park')
const seen = new Set(dictOpts.map((opt) => opt.value))
const options = [...dictOpts]
for (const area of extraParkAreas.value) {
if (area && !seen.has(area)) {
options.push({ label: area, value: area, tagType: 'info' })
seen.add(area)
}
}
const current = form.value.parkArea?.trim()
if (current && !seen.has(current)) {
options.push({ label: current, value: current, tagType: 'info' })
}
return options
})
// 对话框
const dialog = reactive({
@@ -482,17 +452,6 @@ const applyDefaultOwnerUserId = async () => {
form.value.ownerUserId = defaultOwnerUserId as CustomerFormType['ownerUserId']
}
/** 加载历史园区列表 */
const loadParkAreaList = async () => {
try {
const res = await fetchParkAreaList()
const list = unwrapApiData(res as string[] | { data?: string[] })
extraParkAreas.value = Array.isArray(list) ? list.filter(Boolean) : []
} catch (error) {
console.error(error)
extraParkAreas.value = []
}
}
function hasInvoiceInfoFilled() {
const inv = invoiceForm.value
@@ -601,7 +560,7 @@ const open = async (id?: number | string) => {
reset()
dialog.visible = true
await Promise.all([loadUserList(), loadParkAreaList()])
await loadUserList()
if (id) {
dialog.title = '修改客户'
+1 -1
View File
@@ -615,7 +615,7 @@
<el-pagination
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 30, 50]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next, jumper"
:total="pagination.total"
@size-change="getList"
@@ -210,7 +210,7 @@
<el-pagination
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 30, 50]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next, jumper"
:total="pagination.total"
@size-change="getList"
@@ -506,7 +506,7 @@
<el-pagination
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 30, 50]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next, jumper"
:total="pagination.total"
@size-change="getList"
@@ -454,7 +454,7 @@
v-model:current-page="queryParams.pageNum"
v-model:page-size="queryParams.pageSize"
:total="total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next, jumper"
background
@size-change="getList"
@@ -249,7 +249,7 @@
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next, jumper"
background
@size-change="getList"
+1 -1
View File
@@ -275,7 +275,7 @@
<el-pagination
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 30, 50]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next, jumper"
:total="pagination.total"
@size-change="handlePageSizeChange"
@@ -443,7 +443,7 @@
v-model:current-page="queryParams.pageNum"
v-model:page-size="queryParams.pageSize"
:total="total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next, jumper"
@size-change="getList"
@current-change="getList"
@@ -275,18 +275,13 @@
</el-table-column>
<el-table-column label="单位" prop="unit" width="100" align="center">
<template #default="scope">
<el-select
<DictSelect
v-model="scope.row.unit"
dict-type="crm_product_unit"
placeholder="请选择单位"
filterable
allow-create
default-first-option
size="small"
style="width: 100%"
@change="(val) => handleUnitChange(scope.row, val)"
>
<el-option v-for="dict in dictStore.getOptions('crm_product_unit')" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
/>
</template>
</el-table-column>
<el-table-column prop="quantity" width="110">
@@ -321,27 +316,25 @@
<el-table-column prop="packageSpec" min-width="150">
<template #header> <span style="color: #f56c6c">*</span> 包装规格</template>
<template #default="scope">
<el-select
<DictSelect
v-model="scope.row.packageSpec"
dict-type="crm_product_gg"
placeholder="请选择包装规格"
filterable
allow-create
default-first-option
clearable
size="small"
style="width: 100%"
@change="(val) => handlePackageSpecChange(scope.row, val)"
>
<el-option v-for="dict in dictStore.getOptions('crm_product_gg')" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
/>
</template>
</el-table-column>
<el-table-column prop="deliveryMethod" min-width="150">
<template #header> <span style="color: #f56c6c">*</span> 送货方式</template>
<template #default="scope">
<el-select v-model="scope.row.deliveryMethod" placeholder="请选择送货方式" clearable size="small" style="width: 100%">
<el-option v-for="dict in dictStore.getOptions('crm_shfs')" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
<DictSelect
v-model="scope.row.deliveryMethod"
dict-type="crm_shfs"
placeholder="请选择送货方式"
size="small"
style="width: 100%"
/>
</template>
</el-table-column>
<el-table-column label="操作" min-width="80" align="center" header-align="center" fixed="right" class-name="col-actions">
@@ -654,6 +647,7 @@ import {
import ImagePreview from '@/components/ImagePreview/index.vue';
import ProductSelector from '@/components/business/product-selector/index.vue';
import ProductAliasBrandDialog from '@/components/business/product-alias-brand-dialog/index.vue'
import DictSelect from '@/components/DictSelect/index.vue'
import CustomerSelector from '@/components/CustomerSelector.vue';
import ContactSelector from '@/components/ContactSelector.vue';
import ContactFormDialog from '@/views/crm/customer/components/ContactFormDialog.vue'
@@ -1833,16 +1827,6 @@ const normalizeTaxRateDisplay = (value: any, defaultValue = 0): number => {
return Number.isInteger(num) ? num : parseFloat(num.toString());
};
/** 单位变更处理(支持自由输入并写入字典) */
const handleUnitChange = (product: QuoteProductForm, value: string) => {
product.unit = value;
};
/** 包装规格变更处理(支持自由输入并写入字典) */
const handlePackageSpecChange = (product: QuoteProductForm, value: string) => {
product.packageSpec = value;
};
/** 单价变更 */
const handlePriceChange = (product: QuoteProductForm) => {
calculateProductAmount(product);
@@ -67,7 +67,7 @@
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
background
@size-change="loadData"
+1 -1
View File
@@ -376,7 +376,7 @@
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:total="total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next, jumper"
@current-change="handlePageChange"
@size-change="handleSizeChange"
+1 -1
View File
@@ -310,7 +310,7 @@
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:total="total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next, jumper"
@current-change="loadData"
@size-change="loadData"
+1 -1
View File
@@ -326,7 +326,7 @@
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:total="total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next, jumper"
@current-change="handlePageChange"
@size-change="handleSizeChange"
+1 -1
View File
@@ -303,7 +303,7 @@
:current-page="pagination.page"
:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next, jumper"
@update:current-page="
(v) => {
+1 -1
View File
@@ -228,7 +228,7 @@
:current-page="pagination.page"
:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next, jumper"
@update:current-page="(val) => (pagination.page = val)"
@update:page-size="(val) => (pagination.pageSize = val)"
+1 -1
View File
@@ -134,7 +134,7 @@
:current-page="pagination.page"
:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="(val) => ((pagination.page = 1), (pagination.pageSize = val), loadData())"
@current-change="(val) => ((pagination.page = val), loadData())"
@@ -223,7 +223,7 @@
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next, jumper"
@current-change="fetchData"
@size-change="fetchData"
+1 -1
View File
@@ -189,7 +189,7 @@
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="handleSizeChange"
@current-change="handlePageChange"
+1 -1
View File
@@ -163,7 +163,7 @@
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="handleSizeChange"
@current-change="handlePageChange"
@@ -154,7 +154,7 @@
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="handleSizeChange"
@current-change="handlePageChange"
@@ -166,7 +166,7 @@
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="handleSizeChange"
@current-change="handlePageChange"
@@ -143,7 +143,7 @@
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="handleSizeChange"
@current-change="handlePageChange"
@@ -136,7 +136,7 @@
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="handleSizeChange"
@current-change="handlePageChange"
@@ -256,7 +256,7 @@
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="handleSizeChange"
@current-change="handlePageChange"
@@ -199,7 +199,7 @@
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="handleSizeChange"
@current-change="handlePageChange"
+1 -1
View File
@@ -163,7 +163,7 @@
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="handleSizeChange"
@current-change="handlePageChange"
+1 -1
View File
@@ -262,7 +262,7 @@
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="handleSizeChange"
@current-change="handlePageChange"
@@ -175,9 +175,7 @@
align="center"
>
<template #default="{ row }">
<el-tag :type="getPlanTypeTagType(row.planType)" size="small">
{{ getPlanTypeLabel(row.planType) }}
</el-tag>
{{ getPlanTypeLabel(row.planType) }}
</template>
</el-table-column>
<el-table-column
@@ -316,7 +314,7 @@
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="handleSizeChange"
@current-change="handlePageChange"
@@ -517,11 +515,6 @@ const pagination = reactive({
total: 0
})
function getPlanTypeTagType(type: string) {
const map: Record<string, string> = { direct: 'primary', inbound: 'success', outbound: 'warning' }
return map[type] || 'info'
}
function getPlanTypeLabel(type: string) {
const map: Record<string, string> = { direct: '直销单', inbound: '入库单', outbound: '出库单' }
return map[type] || type
+2 -9
View File
@@ -209,9 +209,7 @@
align="center"
>
<template #default="{ row }">
<el-tag :type="getPlanTypeTagType(row.planType)" size="small">
{{ getPlanTypeLabel(row.planType) }}
</el-tag>
{{ getPlanTypeLabel(row.planType) }}
</template>
</el-table-column>
<el-table-column
@@ -351,7 +349,7 @@
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="handleSizeChange"
@current-change="handlePageChange"
@@ -499,11 +497,6 @@ const pagination = reactive({
total: 0
})
function getPlanTypeTagType(type: string) {
const map: Record<string, string> = { direct: 'primary', inbound: 'success', outbound: 'warning' }
return map[type] || 'info'
}
function getPlanTypeLabel(type: string) {
const map: Record<string, string> = { direct: '直销单', inbound: '入库单', outbound: '出库单' }
return map[type] || type
@@ -318,7 +318,7 @@
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next, jumper"
background
@current-change="handlePageChange"
+1 -1
View File
@@ -431,7 +431,7 @@
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="handleSizeChange"
@current-change="handlePageChange"
@@ -237,7 +237,7 @@
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="handleSizeChange"
@current-change="handlePageChange"
@@ -160,7 +160,7 @@
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next, jumper"
@current-change="handlePageChange"
@size-change="handleSizeChange"
+1 -1
View File
@@ -204,7 +204,7 @@
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="handleSizeChange"
@current-change="handlePageChange"
@@ -219,7 +219,7 @@
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="fetchData"
@current-change="fetchData"
@@ -265,7 +265,7 @@
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="fetchData"
@current-change="fetchData"
@@ -189,7 +189,7 @@
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="fetchData"
@current-change="fetchData"
@@ -270,7 +270,7 @@
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="fetchData"
@current-change="fetchData"
@@ -204,7 +204,7 @@
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="fetchData"
@current-change="fetchData"
@@ -250,7 +250,7 @@
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="fetchData"
@current-change="fetchData"
+1 -1
View File
@@ -244,7 +244,7 @@
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="handleSizeChange"
@current-change="handlePageChange"
+2 -2
View File
@@ -148,7 +148,7 @@
:current-page="pagination.page"
:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="(val) => ((pagination.page = 1), (pagination.pageSize = val), loadData())"
@current-change="(val) => ((pagination.page = val), loadData())"
@@ -419,7 +419,7 @@
:current-page="dictDataPagination.page"
:page-size="dictDataPagination.pageSize"
:total="dictDataPagination.total"
:page-sizes="[10, 20, 50]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="
(val) => (
+1 -1
View File
@@ -181,7 +181,7 @@
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="loadData"
@current-change="loadData"
+1 -1
View File
@@ -166,7 +166,7 @@
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next, jumper"
@size-change="fetchList"
@current-change="fetchList"
+1 -1
View File
@@ -155,7 +155,7 @@
:current-page="pagination.page"
:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="(val) => ((pagination.page = 1), (pagination.pageSize = val), loadData())"
@current-change="(val) => ((pagination.page = val), loadData())"
+1 -1
View File
@@ -172,7 +172,7 @@
:current-page="pagination.page"
:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="
(val: number) => ((pagination.page = 1), (pagination.pageSize = val), loadData())
+1 -1
View File
@@ -172,7 +172,7 @@
:current-page="pagination.page"
:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="(val) => ((pagination.page = 1), (pagination.pageSize = val), loadData())"
@current-change="(val) => ((pagination.page = val), loadData())"
+1 -1
View File
@@ -177,7 +177,7 @@
:current-page="pagination.page"
:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="(val) => ((pagination.page = 1), (pagination.pageSize = val), loadData())"
@current-change="(val) => ((pagination.page = val), loadData())"
+1 -1
View File
@@ -229,7 +229,7 @@
:current-page="pagination.page"
:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="
(val: number) => ((pagination.page = 1), (pagination.pageSize = val), loadData())
@@ -134,7 +134,7 @@
:current-page="pagination.page"
:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="(val) => ((pagination.page = 1), (pagination.pageSize = val), loadData())"
@current-change="(val) => ((pagination.page = val), loadData())"
@@ -174,7 +174,7 @@
:current-page="pagination.page"
:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="(val) => ((pagination.page = 1), (pagination.pageSize = val), loadData())"
@current-change="(val) => ((pagination.page = val), loadData())"
@@ -137,7 +137,7 @@
:current-page="pagination.page"
:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="(val) => ((pagination.page = 1), (pagination.pageSize = val), loadData())"
@current-change="(val) => ((pagination.page = val), loadData())"
@@ -132,7 +132,7 @@
:current-page="pagination.page"
:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50]"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="(val) => ((pagination.page = 1), (pagination.pageSize = val), loadData())"
@current-change="(val) => ((pagination.page = val), loadData())"