feat(credit): 更新客户信息编辑组件功能

- 替换城市输入框为省市区级联选择组件
- 新增步骤下拉选择框用于流程状态管理
- 集成文件选择组件替代原有文件路径输入框
- 实现文件上传和删除功能
- 添加文件列表管理和同步逻辑
- 优化搜索组件中关键词搜索框位置
- 完善查询参数过滤逻辑支持数组和单一值处理
This commit is contained in:
2026-03-19 17:08:20 +08:00
parent 72c431967d
commit 56063e9bcd
3 changed files with 160 additions and 38 deletions

View File

@@ -62,12 +62,24 @@
<!-- />--> <!-- />-->
<!-- </a-form-item>--> <!-- </a-form-item>-->
<a-form-item label="所在城市" name="city"> <a-form-item label="所在城市" name="city">
<a-input <RegionsSelect
allow-clear v-model:value="regions"
placeholder="请输入所在城市" type="provinceCity"
v-model:value="form.city" valueField="label"
placeholder="请选择省/市"
/> />
</a-form-item> </a-form-item>
<a-form-item label="步骤" name="step">
<a-select v-model:value="form.step" placeholder="请选择步骤" allow-clear>
<a-select-option
v-for="item in stepOptions"
:key="item.value"
:value="item.value"
>
{{ item.text }}
</a-select-option>
</a-select>
</a-form-item>
<!-- <a-form-item label="所在辖区" name="region">--> <!-- <a-form-item label="所在辖区" name="region">-->
<!-- <a-input--> <!-- <a-input-->
<!-- allow-clear--> <!-- allow-clear-->
@@ -76,10 +88,12 @@
<!-- />--> <!-- />-->
<!-- </a-form-item>--> <!-- </a-form-item>-->
<a-form-item label="文件路径" name="files"> <a-form-item label="文件路径" name="files">
<a-input <SelectFile
allow-clear :data="fileList"
placeholder="请输入文件路径" :limit="9"
v-model:value="form.files" placeholder="请选择附件"
@done="onFileChoose"
@del="onFileDelete"
/> />
</a-form-item> </a-form-item>
<!-- <a-form-item label="是否有数据" name="hasData">--> <!-- <a-form-item label="是否有数据" name="hasData">-->
@@ -131,9 +145,11 @@
import { CreditMpCustomer } from '@/api/credit/creditMpCustomer/model'; import { CreditMpCustomer } from '@/api/credit/creditMpCustomer/model';
import { useThemeStore } from '@/store/modules/theme'; import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form'; import { FormInstance } from 'ant-design-vue/es/form';
import { FileRecord } from '@/api/system/file/model'; import { FileRecord } from '@/api/system/file/model';
import RegionsSelect from '@/components/RegionsSelect/index.vue';
import SelectFile from '@/components/SelectFile/index.vue';
import { isImage } from '@/utils/common';
// 是否是修改 // 是否是修改
const isUpdate = ref(false); const isUpdate = ref(false);
@@ -160,7 +176,17 @@
const maxable = ref(true); const maxable = ref(true);
// 表格选中数据 // 表格选中数据
const formRef = ref<FormInstance | null>(null); const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]); const regions = ref<string[]>();
const fileList = ref<any[]>([]);
const stepOptions = [
{ value: 0, text: '未受理' },
{ value: 1, text: '已受理' },
{ value: 2, text: '材料提交' },
{ value: 3, text: '合同签订' },
{ value: 4, text: '执行回款' },
{ value: 5, text: '完结' }
];
// 用户信息 // 用户信息
const form = reactive<CreditMpCustomer>({ const form = reactive<CreditMpCustomer>({
@@ -175,6 +201,7 @@
city: undefined, city: undefined,
region: undefined, region: undefined,
files: undefined, files: undefined,
step: 0,
hasData: undefined, hasData: undefined,
recommend: undefined, recommend: undefined,
status: undefined, status: undefined,
@@ -204,22 +231,103 @@
] ]
}); });
const chooseImage = (data: FileRecord) => { const guessNameFromUrl = (url: string) => {
images.value.push({ const cleanUrl = (url.split('?')[0] ?? '').trim();
uid: data.id, const last = cleanUrl.split('/').pop() || '';
url: data.path, try {
status: 'done' return decodeURIComponent(last) || url;
}); } catch {
form.files = data.path; return last || url;
}
}; };
const onDeleteItem = (index: number) => { const normalizeFiles = (raw: unknown) => {
images.value.splice(index, 1); if (!raw) return [];
form.files = ''; if (Array.isArray(raw)) {
return raw
.map((item: any) => {
if (!item) return null;
if (typeof item === 'string') {
return {
url: item,
name: guessNameFromUrl(item),
thumbnail: item,
isImage: isImage(item)
};
}
const url = item.url || item.path || item.href;
if (!url || typeof url !== 'string') return null;
return {
url,
name: typeof item.name === 'string' ? item.name : guessNameFromUrl(url),
thumbnail: typeof item.thumbnail === 'string' ? item.thumbnail : undefined,
isImage: typeof item.isImage === 'boolean' ? item.isImage : isImage(url)
};
})
.filter(Boolean);
}
if (typeof raw === 'string') {
const text = raw.trim();
if (!text) return [];
try {
let parsed: any = JSON.parse(text);
if (typeof parsed === 'string') parsed = JSON.parse(parsed);
if (Array.isArray(parsed)) return normalizeFiles(parsed);
} catch {
// ignore
}
return normalizeFiles(text.includes(',') ? text.split(',') : [text]);
}
return [];
};
const syncFilesToForm = () => {
const payload = (fileList.value ?? [])
.map((f: any) => {
const url = f?.url;
if (!url) return null;
return {
name: f?.name || guessNameFromUrl(url),
url,
thumbnail: f?.thumbnail,
isImage: typeof f?.isImage === 'boolean' ? f.isImage : isImage(url)
};
})
.filter(Boolean);
form.files = payload.length ? JSON.stringify(payload) : '';
};
const onFileChoose = (data: FileRecord) => {
const url = data.url || data.downloadUrl || data.path;
if (!url) return;
const exists = fileList.value.some((d: any) => d?.url === url);
if (exists) return;
fileList.value.push({
url,
name: data.name || guessNameFromUrl(url),
thumbnail: data.thumbnail,
isImage: isImage(url)
});
syncFilesToForm();
};
const onFileDelete = (index: number) => {
fileList.value.splice(index, 1);
syncFilesToForm();
}; };
const { resetFields } = useForm(form, rules); const { resetFields } = useForm(form, rules);
// 级联选择回填到表单字段
watch(
regions,
(val) => {
form.province = val?.[0];
form.city = val?.[1];
},
{ immediate: true }
);
/* 保存编辑 */ /* 保存编辑 */
const save = () => { const save = () => {
if (!formRef.value) { if (!formRef.value) {
@@ -252,22 +360,22 @@
() => props.visible, () => props.visible,
(visible) => { (visible) => {
if (visible) { if (visible) {
images.value = []; regions.value = undefined;
fileList.value = [];
if (props.data) { if (props.data) {
assignObject(form, props.data); assignObject(form, props.data);
if(props.data.files){ regions.value = form.province && form.city ? [form.province, form.city] : undefined;
images.value.push({ fileList.value = normalizeFiles(props.data.files);
uid: uuid(), syncFilesToForm();
url: props.data.files,
status: 'done'
})
}
isUpdate.value = true; isUpdate.value = true;
} else { } else {
isUpdate.value = false; isUpdate.value = false;
form.step = 0;
} }
} else { } else {
resetFields(); resetFields();
regions.value = undefined;
fileList.value = [];
} }
}, },
{ immediate: true } { immediate: true }

View File

@@ -24,14 +24,6 @@
</template> </template>
<span>批量删除</span> <span>批量删除</span>
</a-button> </a-button>
<a-input-search
allow-clear
v-model:value="keywords"
placeholder="请输入关键词"
style="width: 220px"
@search="handleSearch"
@pressEnter="handleSearch"
/>
<a-select <a-select
v-model:value="step" v-model:value="step"
allow-clear allow-clear
@@ -47,6 +39,14 @@
{{ item.text }} {{ item.text }}
</a-select-option> </a-select-option>
</a-select> </a-select>
<a-input-search
allow-clear
v-model:value="keywords"
placeholder="请输入关键词"
style="width: 220px"
@search="handleSearch"
@pressEnter="handleSearch"
/>
</a-space> </a-space>
</template> </template>

View File

@@ -143,9 +143,23 @@ const datasource: DatasourceFunction = ({
}) => { }) => {
const params: CreditMpCustomerParam = {...(where as CreditMpCustomerParam)}; const params: CreditMpCustomerParam = {...(where as CreditMpCustomerParam)};
if (filters) { if (filters) {
(params as any).status = filters.status; const filterStatus = (filters as any).status;
if (Array.isArray(filterStatus)) {
if (filterStatus.length) {
(params as any).status = filterStatus[0];
}
} else if (filterStatus !== undefined && filterStatus !== null) {
(params as any).status = filterStatus;
}
const stepFilter = (filters as any).step; const stepFilter = (filters as any).step;
(params as any).step = Array.isArray(stepFilter) ? stepFilter[0] : stepFilter; if (Array.isArray(stepFilter)) {
if (stepFilter.length) {
(params as any).step = stepFilter[0];
}
} else if (stepFilter !== undefined && stepFilter !== null) {
(params as any).step = stepFilter;
}
} }
return pageCreditMpCustomer({ return pageCreditMpCustomer({
...params, ...params,