feat: 添加分析师备注字段并优化代码格式
refactor: 重构文件上传和提交逻辑 style: 调整代码缩进和格式以提高可读性
This commit is contained in:
@@ -24,7 +24,9 @@
|
|||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<n-button secondary strong type="error" round @click="logout" class="logout-btn">
|
<n-button secondary strong type="error" round @click="logout" class="logout-btn">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<n-icon><LogOutOutline /></n-icon>
|
<n-icon>
|
||||||
|
<LogOutOutline />
|
||||||
|
</n-icon>
|
||||||
</template>
|
</template>
|
||||||
<span>退出系统</span>
|
<span>退出系统</span>
|
||||||
</n-button>
|
</n-button>
|
||||||
@@ -58,7 +60,9 @@
|
|||||||
<n-grid-item class="actions-grid">
|
<n-grid-item class="actions-grid">
|
||||||
<n-space justify="end" style="width: 100%">
|
<n-space justify="end" style="width: 100%">
|
||||||
<n-button type="primary" @click="handleSearch" :loading="loading" class="action-btn">
|
<n-button type="primary" @click="handleSearch" :loading="loading" class="action-btn">
|
||||||
<template #icon><n-icon><SearchOutline /></n-icon></template>
|
<template #icon><n-icon>
|
||||||
|
<SearchOutline />
|
||||||
|
</n-icon></template>
|
||||||
查询
|
查询
|
||||||
</n-button>
|
</n-button>
|
||||||
<n-button secondary @click="resetSearch">重置</n-button>
|
<n-button secondary @click="resetSearch">重置</n-button>
|
||||||
@@ -79,24 +83,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #header-extra>
|
<!-- <template #header-extra>
|
||||||
<n-button size="small" strong secondary type="primary" class="export-btn">
|
<n-button size="small" strong secondary type="primary" class="export-btn">
|
||||||
<template #icon><n-icon><DownloadOutline /></n-icon></template>
|
<template #icon><n-icon><DownloadOutline /></n-icon></template>
|
||||||
导出报表
|
导出报表
|
||||||
</n-button>
|
</n-button>
|
||||||
</template>
|
</template> -->
|
||||||
|
|
||||||
<div class="table-container">
|
<div class="table-container">
|
||||||
<n-data-table
|
<n-data-table remote :columns="columns" :data="displayData" :scroll-x="1200" :bordered="false"
|
||||||
remote
|
:pagination="pagination" :loading="loading" :row-class-name="() => 'table-row-animate'" />
|
||||||
:columns="columns"
|
|
||||||
:data="displayData"
|
|
||||||
:scroll-x="1200"
|
|
||||||
:bordered="false"
|
|
||||||
:pagination="pagination"
|
|
||||||
:loading="loading"
|
|
||||||
:row-class-name="() => 'table-row-animate'"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</n-card>
|
</n-card>
|
||||||
</main>
|
</main>
|
||||||
@@ -109,7 +105,9 @@
|
|||||||
<!-- 分配任务卡片 -->
|
<!-- 分配任务卡片 -->
|
||||||
<div class="form-section-card highlight">
|
<div class="form-section-card highlight">
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<n-icon><BrushOutline /></n-icon>
|
<n-icon>
|
||||||
|
<BrushOutline />
|
||||||
|
</n-icon>
|
||||||
<span>执行任务分配</span>
|
<span>执行任务分配</span>
|
||||||
</div>
|
</div>
|
||||||
<n-form label-placement="top" :model="editingRow">
|
<n-form label-placement="top" :model="editingRow">
|
||||||
@@ -131,7 +129,9 @@
|
|||||||
<!-- 基础信息卡片 -->
|
<!-- 基础信息卡片 -->
|
||||||
<div class="form-section-card">
|
<div class="form-section-card">
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<n-icon><PersonOutline /></n-icon>
|
<n-icon>
|
||||||
|
<PersonOutline />
|
||||||
|
</n-icon>
|
||||||
<span>核心业务信息</span>
|
<span>核心业务信息</span>
|
||||||
</div>
|
</div>
|
||||||
<n-form label-placement="left" label-width="90" size="small" :model="editingRow">
|
<n-form label-placement="left" label-width="90" size="small" :model="editingRow">
|
||||||
@@ -144,7 +144,8 @@
|
|||||||
</n-grid-item>
|
</n-grid-item>
|
||||||
<n-grid-item>
|
<n-grid-item>
|
||||||
<n-form-item label="成交日期">
|
<n-form-item label="成交日期">
|
||||||
<n-date-picker v-model:formatted-value="editingRow.transaction_date" value-format="yyyy-MM-dd" type="date" style="width: 100%" />
|
<n-date-picker v-model:formatted-value="editingRow.transaction_date" value-format="yyyy-MM-dd"
|
||||||
|
type="date" style="width: 100%" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</n-grid-item>
|
</n-grid-item>
|
||||||
<n-grid-item>
|
<n-grid-item>
|
||||||
@@ -155,45 +156,38 @@
|
|||||||
</n-grid>
|
</n-grid>
|
||||||
</n-form>
|
</n-form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 附件/素材区 -->
|
<!-- 附件/素材区 -->
|
||||||
<div class="file-grid">
|
<div class="file-grid">
|
||||||
<!-- 1. 附件文档 -->
|
<!-- 1. 附件文档 -->
|
||||||
<div class="file-item">
|
<div class="file-item">
|
||||||
<div class="file-label"><n-icon><FileTrayOutline /></n-icon> 附件文档</div>
|
<div class="file-label"><n-icon>
|
||||||
<n-upload
|
<FileTrayOutline />
|
||||||
v-model:file-list="editingRow.attachmentFileList"
|
</n-icon> 附件文档</div>
|
||||||
:max="1"
|
<n-upload v-model:file-list="editingRow.attachmentFileList" :max="1"
|
||||||
:custom-request="handleCustomUpload"
|
:custom-request="handleCustomUpload">
|
||||||
>
|
|
||||||
<n-button dashed block>更换附件文档</n-button>
|
<n-button dashed block>更换附件文档</n-button>
|
||||||
</n-upload>
|
</n-upload>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 2. 电子签名 -->
|
<!-- 2. 电子签名 -->
|
||||||
<div class="file-item">
|
<div class="file-item">
|
||||||
<div class="file-label"><n-icon><CreateOutline /></n-icon> 电子签名</div>
|
<div class="file-label"><n-icon>
|
||||||
<n-upload
|
<CreateOutline />
|
||||||
v-model:file-list="editingRow.signatureFileList"
|
</n-icon> 电子签名</div>
|
||||||
list-type="image-card"
|
<n-upload v-model:file-list="editingRow.signatureFileList" list-type="image-card" :max="1"
|
||||||
:max="1"
|
@preview="handlePreview" :custom-request="handleCustomUpload">
|
||||||
@preview="handlePreview"
|
|
||||||
:custom-request="handleCustomUpload"
|
|
||||||
>
|
|
||||||
点击上传
|
点击上传
|
||||||
</n-upload>
|
</n-upload>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 3. 付款截图 -->
|
<!-- 3. 付款截图 -->
|
||||||
<div class="file-item payment-item">
|
<div class="file-item payment-item">
|
||||||
<div class="file-label"><n-icon><ImagesOutline /></n-icon> 付款截图凭证 (多选)</div>
|
<div class="file-label"><n-icon>
|
||||||
<n-upload
|
<ImagesOutline />
|
||||||
v-model:file-list="editingRow.paymentFileList"
|
</n-icon> 付款截图凭证 (多选)</div>
|
||||||
list-type="image-card"
|
<n-upload v-model:file-list="editingRow.paymentFileList" list-type="image-card" multiple
|
||||||
multiple
|
@preview="handlePreview" :custom-request="handleCustomUpload" />
|
||||||
@preview="handlePreview"
|
|
||||||
:custom-request="handleCustomUpload"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</n-space>
|
</n-space>
|
||||||
@@ -201,7 +195,8 @@
|
|||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="drawer-footer">
|
<div class="drawer-footer">
|
||||||
<n-button @click="showDrawer = false" round px-8>取消</n-button>
|
<n-button @click="showDrawer = false" round px-8>取消</n-button>
|
||||||
<n-button type="primary" :loading="submitLoading" round @click="handleDrawerSubmit" class="submit-btn-glow">
|
<n-button type="primary" :loading="submitLoading" round @click="handleDrawerSubmit"
|
||||||
|
class="submit-btn-glow">
|
||||||
确认分配并同步数据
|
确认分配并同步数据
|
||||||
</n-button>
|
</n-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -221,14 +216,14 @@
|
|||||||
import { ref, h, reactive, onMounted } from 'vue'
|
import { ref, h, reactive, onMounted } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import {
|
import {
|
||||||
useMessage, zhCN, dateZhCN, NConfigProvider, NCard, NDataTable, NButton, NIcon, NInput,
|
useMessage, zhCN, dateZhCN, NConfigProvider, NCard, NDataTable, NButton, NIcon, NInput,
|
||||||
NDrawer, NDrawerContent, NSpace, NForm, NFormItem, NGrid, NGridItem, NDatePicker, NSelect,
|
NDrawer, NDrawerContent, NSpace, NForm, NFormItem, NGrid, NGridItem, NDatePicker, NSelect,
|
||||||
NTag, NUpload, NModal, NBadge
|
NTag, NUpload, NModal, NBadge
|
||||||
} from 'naive-ui'
|
} from 'naive-ui'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SearchOutline, LogOutOutline, DownloadOutline, PersonOutline, FileTrayOutline,
|
SearchOutline, LogOutOutline, DownloadOutline, PersonOutline, FileTrayOutline,
|
||||||
ImagesOutline, BrushOutline, ReaderOutline, EyeOutline, CreateOutline
|
ImagesOutline, BrushOutline, ReaderOutline, EyeOutline, CreateOutline
|
||||||
} from '@vicons/ionicons5'
|
} from '@vicons/ionicons5'
|
||||||
import http from '@/utils/http'
|
import http from '@/utils/http'
|
||||||
|
|
||||||
@@ -238,219 +233,222 @@ const loading = ref(false)
|
|||||||
const submitLoading = ref(false)
|
const submitLoading = ref(false)
|
||||||
|
|
||||||
// 基础信息表单配置(解决模板中 v-for 太长和语法报错的问题)
|
// 基础信息表单配置(解决模板中 v-for 太长和语法报错的问题)
|
||||||
const baseInfoFields =[
|
const baseInfoFields = [
|
||||||
{ label: '主管', key: 'analyst_supervisor' },
|
{ label: '主管', key: 'analyst_supervisor' },
|
||||||
{ label: '部门', key: 'analyst_department' },
|
{ label: '部门', key: 'analyst_department' },
|
||||||
{ label: '分析师', key: 'analyst_name' },
|
{ label: '分析师', key: 'analyst_name' },
|
||||||
{ label: '家长姓名', key: 'parent_name' },
|
{ label: '家长姓名', key: 'parent_name' },
|
||||||
{ label: '家长电话', key: 'parent_phone' },
|
{ label: '家长电话', key: 'parent_phone' },
|
||||||
{ label: '身份证', key: 'parent_id_card' }
|
{ label: '身份证', key: 'parent_id_card' },
|
||||||
|
{ label: '分析师备注', key: 'analyst_remark' }
|
||||||
]
|
]
|
||||||
|
|
||||||
const searchParams = reactive({
|
const searchParams = reactive({
|
||||||
wecom_id: '',
|
wecom_id: '',
|
||||||
parentInfo: '',
|
parentInfo: '',
|
||||||
dateRange: null,
|
dateRange: null,
|
||||||
status: null
|
status: null
|
||||||
})
|
})
|
||||||
|
|
||||||
const statusOptions =[
|
const statusOptions = [
|
||||||
{ label: '待处理', value: 'pending' },
|
{ label: '待处理', value: 'pending' },
|
||||||
{ label: '已处理', value: 'processed' }
|
{ label: '已处理', value: 'processed' }
|
||||||
]
|
]
|
||||||
|
|
||||||
const displayData = ref([])
|
const displayData = ref([])
|
||||||
|
|
||||||
const pagination = reactive({
|
const pagination = reactive({
|
||||||
page: 1,
|
page: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
itemCount: 0,
|
itemCount: 0,
|
||||||
showSizePicker: true,
|
showSizePicker: true,
|
||||||
pageSizes:[10, 20, 50],
|
pageSizes: [10, 20, 50],
|
||||||
onChange: (page) => {
|
onChange: (page) => {
|
||||||
pagination.page = page
|
pagination.page = page
|
||||||
fetchData()
|
fetchData()
|
||||||
},
|
},
|
||||||
onUpdatePageSize: (pageSize) => {
|
onUpdatePageSize: (pageSize) => {
|
||||||
pagination.pageSize = pageSize
|
pagination.pageSize = pageSize
|
||||||
pagination.page = 1
|
pagination.page = 1
|
||||||
fetchData()
|
fetchData()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 核心逻辑:数据请求与文件对象标准化
|
* 核心逻辑:数据请求与文件对象标准化
|
||||||
*/
|
*/
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const query = new URLSearchParams({
|
const query = new URLSearchParams({
|
||||||
page: pagination.page,
|
page: pagination.page,
|
||||||
page_size: pagination.pageSize
|
page_size: pagination.pageSize
|
||||||
})
|
})
|
||||||
if (searchParams.wecom_id) query.append('wecom_id', searchParams.wecom_id)
|
if (searchParams.wecom_id) query.append('wecom_id', searchParams.wecom_id)
|
||||||
if (searchParams.status) query.append('status', searchParams.status)
|
if (searchParams.status) query.append('status', searchParams.status)
|
||||||
if (searchParams.parentInfo) query.append('parent_name', searchParams.parentInfo)
|
if (searchParams.parentInfo) query.append('parent_name', searchParams.parentInfo)
|
||||||
if (searchParams.dateRange && searchParams.dateRange.length === 2 && searchParams.dateRange[0] && searchParams.dateRange[1]) {
|
if (searchParams.dateRange && searchParams.dateRange.length === 2 && searchParams.dateRange[0] && searchParams.dateRange[1]) {
|
||||||
const startDate = new Date(searchParams.dateRange[0]).toISOString().split('T')[0]
|
const startDate = new Date(searchParams.dateRange[0]).toISOString().split('T')[0]
|
||||||
const endDate = new Date(searchParams.dateRange[1]).toISOString().split('T')[0]
|
const endDate = new Date(searchParams.dateRange[1]).toISOString().split('T')[0]
|
||||||
query.append('start_date', startDate)
|
query.append('start_date', startDate)
|
||||||
query.append('end_date', endDate)
|
query.append('end_date', endDate)
|
||||||
}
|
|
||||||
|
|
||||||
const res = await http.get(`/v1/customer/list?${query.toString()}`)
|
|
||||||
if (res && res.success) {
|
|
||||||
displayData.value = res.data.map(item => {
|
|
||||||
// 列表回显处理:为了让 n-upload 正常显示缩略图,仍然必须组装包含 url 字段的对象
|
|
||||||
const p_names = Array.isArray(item.payment_object_names) ? item.payment_object_names :[]
|
|
||||||
const p_urls = item.payment_image_url ||[]
|
|
||||||
const paymentFileList = p_urls.map((url, i) => ({
|
|
||||||
id: p_names[i] || `pay_${i}`,
|
|
||||||
name: `付款截图_${i + 1}.png`,
|
|
||||||
status: 'finished',
|
|
||||||
url: url, // 映射到组件的 url 以展示缩略图
|
|
||||||
object_name: p_names[i] // 保留真实的 object_name
|
|
||||||
}))
|
|
||||||
|
|
||||||
// 处理签名
|
|
||||||
const signatureFileList = item.signature_image_url ?[{
|
|
||||||
id: item.signature_object_name || 'sig',
|
|
||||||
name: '电子签名.png',
|
|
||||||
status: 'finished',
|
|
||||||
url: item.signature_image_url,
|
|
||||||
object_name: item.signature_object_name
|
|
||||||
}] :[]
|
|
||||||
|
|
||||||
// 处理附件文档
|
|
||||||
const attachmentFileList = item.attachment_file_url ?[{
|
|
||||||
id: item.attachment_object_name || 'att',
|
|
||||||
name: '原始附件文档',
|
|
||||||
status: 'finished',
|
|
||||||
url: item.attachment_file_url,
|
|
||||||
object_name: item.attachment_object_name
|
|
||||||
}] :[]
|
|
||||||
|
|
||||||
return {
|
|
||||||
...item,
|
|
||||||
transaction_amount: item.transaction_amount || '0',
|
|
||||||
assignee_name: item.assignee_name || '',
|
|
||||||
assignee_phone: item.assignee_phone || '',
|
|
||||||
paymentFileList,
|
|
||||||
signatureFileList,
|
|
||||||
attachmentFileList,
|
|
||||||
isSubmitting: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
pagination.itemCount = res.pagination?.total || 0
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Fetch error:', error)
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const res = await http.get(`/v1/customer/list?${query.toString()}`)
|
||||||
|
if (res && res.success) {
|
||||||
|
displayData.value = res.data.map(item => {
|
||||||
|
// 列表回显处理:为了让 n-upload 正常显示缩略图,仍然必须组装包含 url 字段的对象
|
||||||
|
const p_names = Array.isArray(item.payment_object_names) ? item.payment_object_names : []
|
||||||
|
const p_urls = item.payment_image_url || []
|
||||||
|
const paymentFileList = p_urls.map((url, i) => ({
|
||||||
|
id: p_names[i] || `pay_${i}`,
|
||||||
|
name: `付款截图_${i + 1}.png`,
|
||||||
|
status: 'finished',
|
||||||
|
url: url, // 映射到组件的 url 以展示缩略图
|
||||||
|
object_name: p_names[i] // 保留真实的 object_name
|
||||||
|
}))
|
||||||
|
|
||||||
|
// 处理签名
|
||||||
|
const signatureFileList = item.signature_image_url ? [{
|
||||||
|
id: item.signature_object_name || 'sig',
|
||||||
|
name: '电子签名.png',
|
||||||
|
status: 'finished',
|
||||||
|
url: item.signature_image_url,
|
||||||
|
object_name: item.signature_object_name
|
||||||
|
}] : []
|
||||||
|
|
||||||
|
// 处理附件文档
|
||||||
|
const attachmentFileList = item.attachment_file_url ? [{
|
||||||
|
id: item.attachment_object_name || 'att',
|
||||||
|
name: '原始附件文档',
|
||||||
|
status: 'finished',
|
||||||
|
url: item.attachment_file_url,
|
||||||
|
object_name: item.attachment_object_name
|
||||||
|
}] : []
|
||||||
|
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
transaction_amount: item.transaction_amount || '0',
|
||||||
|
assignee_name: item.assignee_name || '',
|
||||||
|
assignee_phone: item.assignee_phone || '',
|
||||||
|
paymentFileList,
|
||||||
|
signatureFileList,
|
||||||
|
attachmentFileList,
|
||||||
|
isSubmitting: false,
|
||||||
|
analyst_remark: item.analyst_remark || ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
pagination.itemCount = res.pagination?.total || 0
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fetch error:', error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 核心上传逻辑:基于引用的响应式更新 (已适配最新接口格式)
|
* 核心上传逻辑:基于引用的响应式更新 (已适配最新接口格式)
|
||||||
*/
|
*/
|
||||||
const handleCustomUpload = async ({ file, onFinish, onError, onProgress }) => {
|
const handleCustomUpload = async ({ file, onFinish, onError, onProgress }) => {
|
||||||
try {
|
try {
|
||||||
const uploadData = new FormData()
|
const uploadData = new FormData()
|
||||||
uploadData.append('file', file.file)
|
uploadData.append('file', file.file)
|
||||||
|
|
||||||
const response = await http.post('/v1/material/upload', uploadData, {
|
const response = await http.post('/v1/material/upload', uploadData, {
|
||||||
headers: { 'Content-Type': 'multipart/form-data' },
|
headers: { 'Content-Type': 'multipart/form-data' },
|
||||||
onUploadProgress: (p) => onProgress({ percent: Math.ceil((p.loaded / p.total) * 100) })
|
onUploadProgress: (p) => onProgress({ percent: Math.ceil((p.loaded / p.total) * 100) })
|
||||||
})
|
})
|
||||||
|
|
||||||
// 提取接口返回的真实数据对象
|
// 提取接口返回的真实数据对象
|
||||||
const resData = response.data
|
const resData = response.data
|
||||||
console.log(24536,resData)
|
console.log(24536, resData)
|
||||||
// 判断是否成功获取到了关键字段 object_name
|
// 判断是否成功获取到了关键字段 object_name
|
||||||
if ( resData && resData.object_name) {
|
if (resData && resData.object_name) {
|
||||||
|
|
||||||
// 1. 核心业务字段赋值:供后续 submitAssignment 提取上传
|
// 1. 核心业务字段赋值:供后续 submitAssignment 提取上传
|
||||||
file.name = resData.object_name
|
file.name = resData.object_name
|
||||||
|
|
||||||
// 2. 组件渲染依赖字段映射:Naive UI 组件强依赖 url 属性显示图片预览,所以把 preview_url 给它
|
// 2. 组件渲染依赖字段映射:Naive UI 组件强依赖 url 属性显示图片预览,所以把 preview_url 给它
|
||||||
file.url = resData.preview_url
|
file.url = resData.preview_url
|
||||||
|
|
||||||
// 3. 存储其余接口返回数据:备用
|
// 3. 存储其余接口返回数据:备用
|
||||||
file.download_url = resData.download_url
|
file.download_url = resData.download_url
|
||||||
file.upload_time = resData.upload_time
|
file.upload_time = resData.upload_time
|
||||||
|
|
||||||
// 更新组件内部状态为完成
|
// 更新组件内部状态为完成
|
||||||
file.status = 'finished'
|
file.status = 'finished'
|
||||||
|
|
||||||
message.success('上传成功')
|
message.success('上传成功')
|
||||||
onFinish()
|
onFinish()
|
||||||
} else {
|
} else {
|
||||||
message.error(response.message || '上传失败,接口返回数据格式异常')
|
message.error(response.message || '上传失败,接口返回数据格式异常')
|
||||||
onError()
|
onError()
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Upload error:', error)
|
|
||||||
message.error('网络错误,上传失败')
|
|
||||||
onError()
|
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Upload error:', error)
|
||||||
|
message.error('网络错误,上传失败')
|
||||||
|
onError()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 提交分配任务 (包含文件标识符提取)
|
* 提交分配任务 (包含文件标识符提取)
|
||||||
*/
|
*/
|
||||||
const submitAssignment = async (row) => {
|
const submitAssignment = async (row) => {
|
||||||
if (!row.assignee_name || !row.assignee_phone) {
|
if (!row.assignee_name || !row.assignee_phone) {
|
||||||
message.warning('请填写指导师姓名和电话')
|
message.warning('请填写指导师姓名和电话')
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查上传状态
|
// 检查上传状态
|
||||||
const allFiles = [...row.paymentFileList, ...row.signatureFileList, ...row.attachmentFileList]
|
const allFiles = [...row.paymentFileList, ...row.signatureFileList, ...row.attachmentFileList]
|
||||||
if (allFiles.some(f => f.status === 'uploading')) {
|
if (allFiles.some(f => f.status === 'uploading')) {
|
||||||
message.warning('文件正在上传中,请稍后提交')
|
message.warning('文件正在上传中,请稍后提交')
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
row.isSubmitting = true
|
row.isSubmitting = true
|
||||||
try {
|
try {
|
||||||
// 从当前数组提取最新的 object_name
|
// 从当前数组提取最新的 object_name
|
||||||
const payment_object_names = row.paymentFileList.map(f => f.name || f?.object_name || f.name).filter(Boolean)
|
const payment_object_names = row.paymentFileList.map(f => f.name || f?.object_name || f.name).filter(Boolean)
|
||||||
const signature_object_name = row.signatureFileList[0]?.name || row.signatureFileList[0]?.object_name || row.signatureFileList[0]?.name
|
const signature_object_name = row.signatureFileList[0]?.name || row.signatureFileList[0]?.object_name || row.signatureFileList[0]?.name
|
||||||
const attachment_object_name = row.attachmentFileList[0]?.name || row.attachmentFileList[0]?.object_name || row.attachmentFileList[0]?.name
|
const attachment_object_name = row.attachmentFileList[0]?.name || row.attachmentFileList[0]?.object_name || row.attachmentFileList[0]?.name
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
wecom_id: String(row.wecom_id),
|
analyst_remark: row.analyst_remark,
|
||||||
payment_object_names,
|
wecom_id: String(row.wecom_id),
|
||||||
signature_object_name,
|
payment_object_names,
|
||||||
attachment_object_name,
|
signature_object_name,
|
||||||
analyst_supervisor: row.analyst_supervisor,
|
attachment_object_name,
|
||||||
analyst_department: row.analyst_department,
|
analyst_supervisor: row.analyst_supervisor,
|
||||||
analyst_name: row.analyst_name,
|
analyst_department: row.analyst_department,
|
||||||
parent_name: row.parent_name,
|
analyst_name: row.analyst_name,
|
||||||
parent_phone: row.parent_phone,
|
parent_name: row.parent_name,
|
||||||
parent_id_card: row.parent_id_card,
|
parent_phone: row.parent_phone,
|
||||||
transaction_date: row.transaction_date,
|
parent_id_card: row.parent_id_card,
|
||||||
transaction_amount: String(row.transaction_amount),
|
transaction_date: row.transaction_date,
|
||||||
guidance_period: row.guidance_period,
|
transaction_amount: String(row.transaction_amount),
|
||||||
assignee_name: row.assignee_name,
|
guidance_period: row.guidance_period,
|
||||||
assignee_phone: row.assignee_phone
|
assignee_name: row.assignee_name,
|
||||||
}
|
assignee_phone: row.assignee_phone
|
||||||
console.log(24531221126,row)
|
|
||||||
console.log('提交的数据:', payload)
|
|
||||||
|
|
||||||
const res = await http.post('/v1/material/submit', payload)
|
|
||||||
if (res && (res.success || res.code === 200)) {
|
|
||||||
message.success('分配并同步成功')
|
|
||||||
row.status = 'processed'
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Submit error:', error)
|
|
||||||
return false
|
|
||||||
} finally {
|
|
||||||
row.isSubmitting = false
|
|
||||||
}
|
}
|
||||||
|
console.log(24531221126, row)
|
||||||
|
console.log('提交的数据:', payload)
|
||||||
|
|
||||||
|
const res = await http.post('/v1/material/submit', payload)
|
||||||
|
if (res && (res.success || res.code === 200)) {
|
||||||
|
message.success('分配并同步成功')
|
||||||
|
row.status = 'processed'
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Submit error:', error)
|
||||||
|
return false
|
||||||
|
} finally {
|
||||||
|
row.isSubmitting = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const showDrawer = ref(false)
|
const showDrawer = ref(false)
|
||||||
@@ -462,113 +460,113 @@ const previewImageUrl = ref('')
|
|||||||
* 打开详情并执行深拷贝隔离
|
* 打开详情并执行深拷贝隔离
|
||||||
*/
|
*/
|
||||||
const openDetails = (row) => {
|
const openDetails = (row) => {
|
||||||
editingRow.value = JSON.parse(JSON.stringify(row))
|
editingRow.value = JSON.parse(JSON.stringify(row))
|
||||||
if (!editingRow.value.transaction_date) {
|
if (!editingRow.value.transaction_date) {
|
||||||
editingRow.value.transaction_date = null
|
editingRow.value.transaction_date = null
|
||||||
}
|
}
|
||||||
showDrawer.value = true
|
showDrawer.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDrawerSubmit = async () => {
|
const handleDrawerSubmit = async () => {
|
||||||
submitLoading.value = true
|
submitLoading.value = true
|
||||||
const success = await submitAssignment(editingRow.value)
|
const success = await submitAssignment(editingRow.value)
|
||||||
submitLoading.value = false
|
submitLoading.value = false
|
||||||
if (success) {
|
if (success) {
|
||||||
showDrawer.value = false
|
showDrawer.value = false
|
||||||
fetchData()
|
fetchData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 表格列配置
|
* 表格列配置
|
||||||
*/
|
*/
|
||||||
const columns =[
|
const columns = [
|
||||||
{ title: '分析师', key: 'analyst_name', width: 100, fixed: 'left' },
|
{ title: '分析师', key: 'analyst_name', width: 100, fixed: 'left' },
|
||||||
{ title: '家长姓名', key: 'parent_name', width: 100 },
|
{ title: '家长姓名', key: 'parent_name', width: 100 },
|
||||||
{
|
{
|
||||||
title: '状态',
|
title: '状态',
|
||||||
key: 'status',
|
key: 'status',
|
||||||
width: 100,
|
width: 100,
|
||||||
render: (row) => h(NTag, {
|
render: (row) => h(NTag, {
|
||||||
type: row.status === 'pending' ? 'warning' : 'success',
|
type: row.status === 'pending' ? 'warning' : 'success',
|
||||||
variant: 'outline',
|
variant: 'outline',
|
||||||
round: true,
|
round: true,
|
||||||
size: 'small'
|
size: 'small'
|
||||||
}, { default: () => row.status === 'pending' ? '待处理' : '已处理' })
|
}, { default: () => row.status === 'pending' ? '待处理' : '已处理' })
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '成交金额',
|
title: '成交金额',
|
||||||
key: 'transaction_amount',
|
key: 'transaction_amount',
|
||||||
width: 120,
|
width: 120,
|
||||||
render: (row) => h('span', { style: 'color: #e88080; font-weight: 600' }, `¥${row.transaction_amount}`)
|
render: (row) => h('span', { style: 'color: #e88080; font-weight: 600' }, `¥${row.transaction_amount}`)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '分配指导师',
|
title: '分配指导师',
|
||||||
key: 'assignee_name',
|
key: 'assignee_name',
|
||||||
width: 150,
|
width: 150,
|
||||||
render: (row) => h(NInput, {
|
render: (row) => h(NInput, {
|
||||||
value: row.assignee_name,
|
value: row.assignee_name,
|
||||||
size: 'small',
|
size: 'small',
|
||||||
placeholder: '输入姓名',
|
placeholder: '输入姓名',
|
||||||
style: 'border-radius: 6px',
|
style: 'border-radius: 6px',
|
||||||
onUpdateValue: (v) => { row.assignee_name = v }
|
onUpdateValue: (v) => { row.assignee_name = v }
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '指导师电话',
|
title: '指导师电话',
|
||||||
key: 'assignee_phone',
|
key: 'assignee_phone',
|
||||||
width: 180,
|
width: 180,
|
||||||
render: (row) => h(NInput, {
|
render: (row) => h(NInput, {
|
||||||
value: row.assignee_phone,
|
value: row.assignee_phone,
|
||||||
size: 'small',
|
size: 'small',
|
||||||
placeholder: '输入手机号',
|
placeholder: '输入手机号',
|
||||||
style: 'border-radius: 6px',
|
style: 'border-radius: 6px',
|
||||||
onUpdateValue: (v) => { row.assignee_phone = v }
|
onUpdateValue: (v) => { row.assignee_phone = v }
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
key: 'op',
|
key: 'op',
|
||||||
width: 150,
|
width: 150,
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
render: (row) => h(NSpace, { justify: 'center' }, {
|
render: (row) => h(NSpace, { justify: 'center' }, {
|
||||||
default: () =>[
|
default: () => [
|
||||||
h(NButton, {
|
h(NButton, {
|
||||||
circle: true,
|
circle: true,
|
||||||
quaternary: true,
|
quaternary: true,
|
||||||
onClick: () => openDetails(row)
|
onClick: () => openDetails(row)
|
||||||
}, { icon: () => h(NIcon, null, { default: () => h(EyeOutline) }) }),
|
}, { icon: () => h(NIcon, null, { default: () => h(EyeOutline) }) }),
|
||||||
h(NButton, {
|
h(NButton, {
|
||||||
type: 'primary',
|
type: 'primary',
|
||||||
round: true,
|
round: true,
|
||||||
size: 'small',
|
size: 'small',
|
||||||
loading: row.isSubmitting,
|
loading: row.isSubmitting,
|
||||||
onClick: () => submitAssignment(row)
|
onClick: () => submitAssignment(row)
|
||||||
}, { default: () => '分配' })
|
}, { default: () => '分配' })
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
// 预览依然取 url,因为 handleCustomUpload 已将接口真实的 preview_url 映射给了 url
|
// 预览依然取 url,因为 handleCustomUpload 已将接口真实的 preview_url 映射给了 url
|
||||||
const handlePreview = (file) => {
|
const handlePreview = (file) => {
|
||||||
previewImageUrl.value = file.url || (file.file && URL.createObjectURL(file.file))
|
previewImageUrl.value = file.url || (file.file && URL.createObjectURL(file.file))
|
||||||
showPreview.value = true
|
showPreview.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSearch = () => { pagination.page = 1; fetchData() }
|
const handleSearch = () => { pagination.page = 1; fetchData() }
|
||||||
const resetSearch = () => {
|
const resetSearch = () => {
|
||||||
Object.assign(searchParams, { wecom_id: '', parentInfo: '', dateRange: null, status: null })
|
Object.assign(searchParams, { wecom_id: '', parentInfo: '', dateRange: null, status: null })
|
||||||
pagination.page = 1
|
pagination.page = 1
|
||||||
fetchData()
|
fetchData()
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => fetchData())
|
onMounted(() => fetchData())
|
||||||
|
|
||||||
const logout = () => {
|
const logout = () => {
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
router.push('/login')
|
router.push('/login')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -601,8 +599,19 @@ const logout = () => {
|
|||||||
filter: blur(40px);
|
filter: blur(40px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.circle-1 { width: 300px; height: 300px; top: -100px; right: -50px; }
|
.circle-1 {
|
||||||
.circle-2 { width: 200px; height: 200px; bottom: -50px; left: 10%; }
|
width: 300px;
|
||||||
|
height: 300px;
|
||||||
|
top: -100px;
|
||||||
|
right: -50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circle-2 {
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
bottom: -50px;
|
||||||
|
left: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
/* 内容包裹器 */
|
/* 内容包裹器 */
|
||||||
.content-wrapper {
|
.content-wrapper {
|
||||||
@@ -779,16 +788,30 @@ const logout = () => {
|
|||||||
|
|
||||||
/* 响应式适配 */
|
/* 响应式适配 */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.header-banner { height: 180px; }
|
.header-banner {
|
||||||
.title-meta h1 { font-size: 18px; }
|
height: 180px;
|
||||||
.title-meta p { display: none; }
|
}
|
||||||
.header-right span { display: none; }
|
|
||||||
.logout-btn { padding: 8px !important; }
|
.title-meta h1 {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-meta p {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-right span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-btn {
|
||||||
|
padding: 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
.file-grid {
|
.file-grid {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
.payment-item {
|
.payment-item {
|
||||||
grid-column: span 1;
|
grid-column: span 1;
|
||||||
}
|
}
|
||||||
@@ -798,7 +821,7 @@ const logout = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 移动端表格隐藏部分列,防止挤压 */
|
/* 移动端表格隐藏部分列,防止挤压 */
|
||||||
:deep(.n-data-table-th:nth-child(2)),
|
:deep(.n-data-table-th:nth-child(2)),
|
||||||
:deep(.n-data-table-td:nth-child(2)) {
|
:deep(.n-data-table-td:nth-child(2)) {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<n-button class="logout-btn" secondary type="error" round @click="logout">
|
<n-button class="logout-btn" secondary type="error" round @click="logout">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<n-icon><LogOutOutline /></n-icon>
|
<n-icon>
|
||||||
|
<LogOutOutline />
|
||||||
|
</n-icon>
|
||||||
</template>
|
</template>
|
||||||
退出系统
|
退出系统
|
||||||
</n-button>
|
</n-button>
|
||||||
@@ -28,24 +30,28 @@
|
|||||||
<div class="main-content">
|
<div class="main-content">
|
||||||
<n-form ref="formRef" :model="formData" :rules="rules" label-placement="top"
|
<n-form ref="formRef" :model="formData" :rules="rules" label-placement="top"
|
||||||
require-mark-placement="right-hanging">
|
require-mark-placement="right-hanging">
|
||||||
|
|
||||||
<!-- 模块 1: 基础信息卡片 -->
|
<!-- 模块 1: 基础信息卡片 -->
|
||||||
<div class="glass-card section-card">
|
<div class="glass-card section-card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="header-indicator"></div>
|
<div class="header-indicator"></div>
|
||||||
<n-icon size="22"><IdCardOutline /></n-icon>
|
<n-icon size="22">
|
||||||
|
<IdCardOutline />
|
||||||
|
</n-icon>
|
||||||
<h3>1. 客户及成交信息</h3>
|
<h3>1. 客户及成交信息</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<n-grid :x-gap="24" :y-gap="0" cols="1 s:2 m:3" responsive="screen">
|
<n-grid :x-gap="24" :y-gap="0" cols="1 s:2 m:3" responsive="screen">
|
||||||
<n-grid-item>
|
<n-grid-item>
|
||||||
<n-form-item label="分析师主管" path="analystSupervisor">
|
<n-form-item label="分析师主管" path="analystSupervisor">
|
||||||
<n-input v-model:value="formData.analystSupervisor" placeholder="输入姓名" clearable />
|
<n-input v-model:value="formData.analystSupervisor" placeholder="输入姓名"
|
||||||
|
clearable />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</n-grid-item>
|
</n-grid-item>
|
||||||
<n-grid-item>
|
<n-grid-item>
|
||||||
<n-form-item label="分析师部门" path="analystDepartment">
|
<n-form-item label="分析师部门" path="analystDepartment">
|
||||||
<n-input v-model:value="formData.analystDepartment" placeholder="例如:市场一部" clearable />
|
<n-input v-model:value="formData.analystDepartment" placeholder="例如:市场一部"
|
||||||
|
clearable />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</n-grid-item>
|
</n-grid-item>
|
||||||
<n-grid-item>
|
<n-grid-item>
|
||||||
@@ -65,29 +71,34 @@
|
|||||||
</n-grid-item>
|
</n-grid-item>
|
||||||
<n-grid-item>
|
<n-grid-item>
|
||||||
<n-form-item label="身份证号码" path="parentIdCard">
|
<n-form-item label="身份证号码" path="parentIdCard">
|
||||||
<n-input v-model:value="formData.parentIdCard" placeholder="18位身份证号" clearable />
|
<n-input v-model:value="formData.parentIdCard" placeholder="18位身份证号"
|
||||||
|
clearable />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</n-grid-item>
|
</n-grid-item>
|
||||||
<n-grid-item>
|
<n-grid-item>
|
||||||
<n-form-item label="成交日期" path="transactionDate">
|
<n-form-item label="成交日期" path="transactionDate">
|
||||||
<n-date-picker v-model:value="formData.transactionDate" type="date" clearable style="width: 100%" />
|
<n-date-picker v-model:value="formData.transactionDate" type="date" clearable
|
||||||
|
style="width: 100%" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</n-grid-item>
|
</n-grid-item>
|
||||||
<n-grid-item>
|
<n-grid-item>
|
||||||
<n-form-item label="成交金额" path="transactionAmount">
|
<n-form-item label="成交金额" path="transactionAmount">
|
||||||
<n-input-number v-model:value="formData.transactionAmount" placeholder="0.00" clearable style="width: 100%">
|
<n-input-number v-model:value="formData.transactionAmount" placeholder="0.00"
|
||||||
|
clearable style="width: 100%">
|
||||||
<template #prefix>¥</template>
|
<template #prefix>¥</template>
|
||||||
</n-input-number>
|
</n-input-number>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</n-grid-item>
|
</n-grid-item>
|
||||||
<n-grid-item>
|
<n-grid-item>
|
||||||
<n-form-item label="指导周期" path="guidancePeriod">
|
<n-form-item label="指导周期" path="guidancePeriod">
|
||||||
<n-input v-model:value="formData.guidancePeriod" placeholder="例如:3个月" clearable />
|
<n-input v-model:value="formData.guidancePeriod" placeholder="例如:3个月"
|
||||||
|
clearable />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</n-grid-item>
|
</n-grid-item>
|
||||||
</n-grid>
|
</n-grid>
|
||||||
<n-form-item label="分析师备注" class="mt-8">
|
<n-form-item label="分析师备注" class="mt-8">
|
||||||
<n-input v-model:value="formData.analystNotes" type="textarea" placeholder="填写备注信息(选填)" :autosize="{ minRows: 2, maxRows: 4 }" />
|
<n-input v-model:value="formData.analystNotes" type="textarea" placeholder="填写备注信息(选填)"
|
||||||
|
:autosize="{ minRows: 2, maxRows: 4 }" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -98,7 +109,9 @@
|
|||||||
<div class="glass-card section-card full-h">
|
<div class="glass-card section-card full-h">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="header-indicator"></div>
|
<div class="header-indicator"></div>
|
||||||
<n-icon size="22"><DocumentAttachOutline /></n-icon>
|
<n-icon size="22">
|
||||||
|
<DocumentAttachOutline />
|
||||||
|
</n-icon>
|
||||||
<h3>2. 附件文档 (限1份)</h3>
|
<h3>2. 附件文档 (限1份)</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
@@ -107,7 +120,9 @@
|
|||||||
:custom-request="({ file, onFinish, onError, onProgress }) => handleCustomUpload({ file, onFinish, onError, onProgress }, 'documentFileList')">
|
:custom-request="({ file, onFinish, onError, onProgress }) => handleCustomUpload({ file, onFinish, onError, onProgress }, 'documentFileList')">
|
||||||
<n-upload-dragger class="modern-dragger">
|
<n-upload-dragger class="modern-dragger">
|
||||||
<div class="dragger-icon">
|
<div class="dragger-icon">
|
||||||
<n-icon size="44" depth="3"><CloudUploadOutline /></n-icon>
|
<n-icon size="44" depth="3">
|
||||||
|
<CloudUploadOutline />
|
||||||
|
</n-icon>
|
||||||
</div>
|
</div>
|
||||||
<n-text class="dragger-text">点击或拖拽上传</n-text>
|
<n-text class="dragger-text">点击或拖拽上传</n-text>
|
||||||
<n-p depth="3" class="dragger-subtext">支持 PDF, DOCX, XLSX (Max 50MB)</n-p>
|
<n-p depth="3" class="dragger-subtext">支持 PDF, DOCX, XLSX (Max 50MB)</n-p>
|
||||||
@@ -120,7 +135,9 @@
|
|||||||
<div class="glass-card section-card full-h">
|
<div class="glass-card section-card full-h">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="header-indicator"></div>
|
<div class="header-indicator"></div>
|
||||||
<n-icon size="22"><BrushOutline /></n-icon>
|
<n-icon size="22">
|
||||||
|
<BrushOutline />
|
||||||
|
</n-icon>
|
||||||
<h3>4. 电子签名 (限1张)</h3>
|
<h3>4. 电子签名 (限1张)</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
@@ -129,7 +146,9 @@
|
|||||||
@preview="handlePreview"
|
@preview="handlePreview"
|
||||||
:custom-request="({ file, onFinish, onError, onProgress }) => handleCustomUpload({ file, onFinish, onError, onProgress }, 'signatureFileList')">
|
:custom-request="({ file, onFinish, onError, onProgress }) => handleCustomUpload({ file, onFinish, onError, onProgress }, 'signatureFileList')">
|
||||||
<div class="signature-placeholder">
|
<div class="signature-placeholder">
|
||||||
<n-icon size="28" depth="3"><CreateOutline /></n-icon>
|
<n-icon size="28" depth="3">
|
||||||
|
<CreateOutline />
|
||||||
|
</n-icon>
|
||||||
<span>上传手写签名</span>
|
<span>上传手写签名</span>
|
||||||
</div>
|
</div>
|
||||||
</n-upload>
|
</n-upload>
|
||||||
@@ -142,7 +161,9 @@
|
|||||||
<div class="glass-card section-card mt-20">
|
<div class="glass-card section-card mt-20">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="header-indicator"></div>
|
<div class="header-indicator"></div>
|
||||||
<n-icon size="22"><ImagesOutline /></n-icon>
|
<n-icon size="22">
|
||||||
|
<ImagesOutline />
|
||||||
|
</n-icon>
|
||||||
<h3>3. 付款截图凭证 (可多张)</h3>
|
<h3>3. 付款截图凭证 (可多张)</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
@@ -150,7 +171,9 @@
|
|||||||
v-model:file-list="paymentFileListLast" @preview="handlePreview"
|
v-model:file-list="paymentFileListLast" @preview="handlePreview"
|
||||||
:custom-request="({ file, onFinish, onError, onProgress }) => handleCustomUpload({ file, onFinish, onError, onProgress }, 'paymentFileList')">
|
:custom-request="({ file, onFinish, onError, onProgress }) => handleCustomUpload({ file, onFinish, onError, onProgress }, 'paymentFileList')">
|
||||||
<div class="payment-placeholder">
|
<div class="payment-placeholder">
|
||||||
<n-icon size="32"><CameraOutline /></n-icon>
|
<n-icon size="32">
|
||||||
|
<CameraOutline />
|
||||||
|
</n-icon>
|
||||||
<span>添加凭证</span>
|
<span>添加凭证</span>
|
||||||
</div>
|
</div>
|
||||||
</n-upload>
|
</n-upload>
|
||||||
@@ -167,7 +190,9 @@
|
|||||||
</n-button>
|
</n-button>
|
||||||
<n-button size="large" type="primary" round @click="handleSubmit" class="btn-submit">
|
<n-button size="large" type="primary" round @click="handleSubmit" class="btn-submit">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<n-icon><CheckmarkCircleOutline /></n-icon>
|
<n-icon>
|
||||||
|
<CheckmarkCircleOutline />
|
||||||
|
</n-icon>
|
||||||
</template>
|
</template>
|
||||||
提交材料至中心
|
提交材料至中心
|
||||||
</n-button>
|
</n-button>
|
||||||
@@ -220,39 +245,39 @@ const themeOverrides = {
|
|||||||
* 初始化企业微信 JSSDK
|
* 初始化企业微信 JSSDK
|
||||||
*/
|
*/
|
||||||
async function getConfigSignature(url) {
|
async function getConfigSignature(url) {
|
||||||
const response = await fetch('https://superdata.nycjy.cn/api/v1/wecom/agent-config-signature', {
|
const response = await fetch('https://superdata.nycjy.cn/api/v1/wecom/agent-config-signature', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ url })
|
body: JSON.stringify({ url })
|
||||||
})
|
})
|
||||||
const ConfigSignature = await response.json()
|
const ConfigSignature = await response.json()
|
||||||
console.log('基础签名响应:', ConfigSignature)
|
console.log('基础签名响应:', ConfigSignature)
|
||||||
if (ConfigSignature.code === 200 && ConfigSignature.data) {
|
if (ConfigSignature.code === 200 && ConfigSignature.data) {
|
||||||
return {
|
return {
|
||||||
timestamp: ConfigSignature.data.timestamp,
|
timestamp: ConfigSignature.data.timestamp,
|
||||||
nonceStr: ConfigSignature.data.nonceStr,
|
nonceStr: ConfigSignature.data.nonceStr,
|
||||||
signature: ConfigSignature.data.corpSignature
|
signature: ConfigSignature.data.corpSignature
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
throw new Error('基础签名获取失败')
|
||||||
throw new Error('基础签名获取失败')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getAgentConfigSignature(url) {
|
async function getAgentConfigSignature(url) {
|
||||||
const response = await fetch('https://superdata.nycjy.cn/api/v1/wecom/agent-config-signature', {
|
const response = await fetch('https://superdata.nycjy.cn/api/v1/wecom/agent-config-signature', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ url })
|
body: JSON.stringify({ url })
|
||||||
})
|
})
|
||||||
const AgentConfigSignature = await response.json()
|
const AgentConfigSignature = await response.json()
|
||||||
console.log('应用签名响应:', AgentConfigSignature)
|
console.log('应用签名响应:', AgentConfigSignature)
|
||||||
if (AgentConfigSignature.code === 200 && AgentConfigSignature.data) {
|
if (AgentConfigSignature.code === 200 && AgentConfigSignature.data) {
|
||||||
return {
|
return {
|
||||||
timestamp: AgentConfigSignature.data.timestamp,
|
timestamp: AgentConfigSignature.data.timestamp,
|
||||||
nonceStr: AgentConfigSignature.data.nonceStr,
|
nonceStr: AgentConfigSignature.data.nonceStr,
|
||||||
signature: AgentConfigSignature.data.signature
|
signature: AgentConfigSignature.data.signature
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
throw new Error('应用签名获取失败')
|
||||||
throw new Error('应用签名获取失败')
|
|
||||||
}
|
}
|
||||||
const initSDK = async () => {
|
const initSDK = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -433,7 +458,7 @@ const handleSubmit = async () => {
|
|||||||
message.error('未获取到客户ID,无法提交');
|
message.error('未获取到客户ID,无法提交');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const submitPayload = {
|
const submitPayload = {
|
||||||
wecom_id: wecomId,
|
wecom_id: wecomId,
|
||||||
analyst_supervisor: formData.analystSupervisor,
|
analyst_supervisor: formData.analystSupervisor,
|
||||||
@@ -445,6 +470,7 @@ const handleSubmit = async () => {
|
|||||||
transaction_date: formatDate(formData.transactionDate),
|
transaction_date: formatDate(formData.transactionDate),
|
||||||
transaction_amount: String(formData.transactionAmount || ''),
|
transaction_amount: String(formData.transactionAmount || ''),
|
||||||
guidance_period: formData.guidancePeriod,
|
guidance_period: formData.guidancePeriod,
|
||||||
|
analyst_remark: formData.analystNotes,
|
||||||
payment_object_names: formData.paymentFileList.map(f => f.rawResponse?.data?.object_name).filter(Boolean),
|
payment_object_names: formData.paymentFileList.map(f => f.rawResponse?.data?.object_name).filter(Boolean),
|
||||||
signature_object_name: formData.signatureFileList[0]?.rawResponse?.data?.object_name || null,
|
signature_object_name: formData.signatureFileList[0]?.rawResponse?.data?.object_name || null,
|
||||||
attachment_object_name: formData.documentFileList[0]?.rawResponse?.data?.object_name || null,
|
attachment_object_name: formData.documentFileList[0]?.rawResponse?.data?.object_name || null,
|
||||||
@@ -471,7 +497,8 @@ const handleCancel = () => router.back()
|
|||||||
.page-wrapper {
|
.page-wrapper {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background-color: #f5f7fa;
|
background-color: #f5f7fa;
|
||||||
padding-bottom: 120px; /* 为底部悬浮栏留出空间 */
|
padding-bottom: 120px;
|
||||||
|
/* 为底部悬浮栏留出空间 */
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -580,9 +607,19 @@ const handleCancel = () => router.back()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 布局辅助 */
|
/* 布局辅助 */
|
||||||
.mt-8 { margin-top: 8px; }
|
.mt-8 {
|
||||||
.mt-20 { margin-top: 20px; }
|
margin-top: 8px;
|
||||||
.full-h { height: 100%; display: flex; flex-direction: column; }
|
}
|
||||||
|
|
||||||
|
.mt-20 {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-h {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
/* 上传组件现代风格 */
|
/* 上传组件现代风格 */
|
||||||
.modern-dragger {
|
.modern-dragger {
|
||||||
@@ -641,6 +678,7 @@ const handleCancel = () => router.back()
|
|||||||
height: 120px;
|
height: 120px;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.payment-modern-upload :deep(.n-upload-file.n-upload-file--image-card) {
|
.payment-modern-upload :deep(.n-upload-file.n-upload-file--image-card) {
|
||||||
width: 120px;
|
width: 120px;
|
||||||
height: 120px;
|
height: 120px;
|
||||||
@@ -705,17 +743,27 @@ const handleCancel = () => router.back()
|
|||||||
height: 180px;
|
height: 180px;
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-group h1 { font-size: 20px; }
|
.text-group h1 {
|
||||||
.text-group p { font-size: 11px; }
|
font-size: 20px;
|
||||||
.logout-btn { scale: 0.9; }
|
}
|
||||||
|
|
||||||
|
.text-group p {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-btn {
|
||||||
|
scale: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
.main-content {
|
.main-content {
|
||||||
margin-top: -40px;
|
margin-top: -40px;
|
||||||
padding: 0 12px;
|
padding: 0 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-body { padding: 16px; }
|
.card-body {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
.footer-actions-wrapper {
|
.footer-actions-wrapper {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
@@ -723,9 +771,19 @@ const handleCancel = () => router.back()
|
|||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
height: 80px;
|
height: 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-blur-bg { border-radius: 20px 20px 0 0; }
|
.footer-blur-bg {
|
||||||
.footer-content { justify-content: space-around; padding: 0 16px; }
|
border-radius: 20px 20px 0 0;
|
||||||
.btn-cancel, .btn-submit { flex: 1; }
|
}
|
||||||
|
|
||||||
|
.footer-content {
|
||||||
|
justify-content: space-around;
|
||||||
|
padding: 0 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-cancel,
|
||||||
|
.btn-submit {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
Reference in New Issue
Block a user