Merge branch 'main' of https://git.1024tool.vip/zfc/guzhi
This commit is contained in:
commit
f17c1678c8
@ -46,7 +46,8 @@ const activeDetailTab = ref('audit')
|
|||||||
const reportLoading = ref(false)
|
const reportLoading = ref(false)
|
||||||
const reportContent = ref('')
|
const reportContent = ref('')
|
||||||
|
|
||||||
const pickFilledValue = (...values) => values.find((val) => val !== undefined && val !== null && val !== '')
|
const pickFilledValue = (...values) =>
|
||||||
|
values.find((val) => val !== undefined && val !== null && val !== '')
|
||||||
const formatEnumField = (key, ...values) => formatEnumByKey(pickFilledValue(...values), key)
|
const formatEnumField = (key, ...values) => formatEnumByKey(pickFilledValue(...values), key)
|
||||||
|
|
||||||
// 证书弹窗相关状态
|
// 证书弹窗相关状态
|
||||||
@ -97,7 +98,11 @@ const detailSections = computed(() => {
|
|||||||
fields: [
|
fields: [
|
||||||
{ label: '资产名称', type: 'text', value: detail.asset_name || '-' },
|
{ label: '资产名称', type: 'text', value: detail.asset_name || '-' },
|
||||||
{ label: '所属机构/权利人', type: 'text', value: detail.institution || '-' },
|
{ label: '所属机构/权利人', type: 'text', value: detail.institution || '-' },
|
||||||
{ label: '统一社会信用代码/身份证号', type: 'text', value: detail.credit_code_or_id || '-' },
|
{
|
||||||
|
label: '统一社会信用代码/身份证号',
|
||||||
|
type: 'text',
|
||||||
|
value: detail.credit_code_or_id || '-',
|
||||||
|
},
|
||||||
{ label: '所属行业', type: 'text', value: detail.industry || '-' },
|
{ label: '所属行业', type: 'text', value: detail.industry || '-' },
|
||||||
{ label: '业务/传承介绍', type: 'text', value: detail.biz_intro || '-' },
|
{ label: '业务/传承介绍', type: 'text', value: detail.biz_intro || '-' },
|
||||||
],
|
],
|
||||||
@ -106,17 +111,37 @@ const detailSections = computed(() => {
|
|||||||
key: 'finance',
|
key: 'finance',
|
||||||
title: '财务状况',
|
title: '财务状况',
|
||||||
fields: [
|
fields: [
|
||||||
{ label: '近12个月机构营收/万元', type: 'text', value: formatNumberValue(detail.annual_revenue) },
|
{
|
||||||
{ label: '近12个月机构研发投入/万元', type: 'text', value: formatNumberValue(detail.rd_investment) },
|
label: '近12个月机构营收/万元',
|
||||||
{ label: '近三年机构收益/万元', type: 'list', value: formatThreeYearIncome(detail.three_year_income) },
|
type: 'text',
|
||||||
{ label: '资产受资助情况', type: 'text', value: formatEnumField('fundingStatus', detail.funding_status) },
|
value: formatNumberValue(detail.annual_revenue),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '近12个月机构研发投入/万元',
|
||||||
|
type: 'text',
|
||||||
|
value: formatNumberValue(detail.rd_investment),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '近三年机构收益/万元',
|
||||||
|
type: 'list',
|
||||||
|
value: formatThreeYearIncome(detail.three_year_income),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '资产受资助情况',
|
||||||
|
type: 'text',
|
||||||
|
value: formatEnumField('fundingStatus', detail.funding_status),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'tech',
|
key: 'tech',
|
||||||
title: '非遗等级与技术',
|
title: '非遗等级与技术',
|
||||||
fields: [
|
fields: [
|
||||||
{ label: '非遗传承人等级', type: 'text', value: formatEnumField('inheritorLevel', detail.inheritor_level) },
|
{
|
||||||
|
label: '非遗传承人等级',
|
||||||
|
type: 'text',
|
||||||
|
value: formatEnumField('inheritorLevel', detail.inheritor_level),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '非遗传承人年龄水平及数量',
|
label: '非遗传承人年龄水平及数量',
|
||||||
type: 'list',
|
type: 'list',
|
||||||
@ -126,10 +151,22 @@ const detailSections = computed(() => {
|
|||||||
{
|
{
|
||||||
label: '非遗等级',
|
label: '非遗等级',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
value: formatEnumField('heritageLevel', detail.heritage_level, detail.heritage_asset_level),
|
value: formatEnumField(
|
||||||
|
'heritageLevel',
|
||||||
|
detail.heritage_level,
|
||||||
|
detail.heritage_asset_level
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '非遗资产所用专利的申请号',
|
||||||
|
type: 'text',
|
||||||
|
value: detail.patent_application_no || '-',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '非遗资产历史证明证据及数量',
|
||||||
|
type: 'list',
|
||||||
|
value: formatHistoricalEvidence(detail.historical_evidence),
|
||||||
},
|
},
|
||||||
{ label: '非遗资产所用专利的申请号', type: 'text', value: detail.patent_application_no || '-' },
|
|
||||||
{ label: '非遗资产历史证明证据及数量', type: 'list', value: formatHistoricalEvidence(detail.historical_evidence) },
|
|
||||||
{
|
{
|
||||||
label: '非遗资产所用专利/纹样图片',
|
label: '非遗资产所用专利/纹样图片',
|
||||||
type: 'images',
|
type: 'images',
|
||||||
@ -144,50 +181,86 @@ const detailSections = computed(() => {
|
|||||||
{
|
{
|
||||||
label: '非遗资产应用成熟度',
|
label: '非遗资产应用成熟度',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
value: formatEnumField('applicationMaturity', detail.application_maturity, detail.implementation_stage),
|
value: formatEnumField(
|
||||||
|
'applicationMaturity',
|
||||||
|
detail.application_maturity,
|
||||||
|
detail.implementation_stage
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '非遗资产应用覆盖范围',
|
label: '非遗资产应用覆盖范围',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
value: formatEnumField('applicationCoverage', detail.application_coverage, detail.coverage_area),
|
value: formatEnumField(
|
||||||
|
'applicationCoverage',
|
||||||
|
detail.application_coverage,
|
||||||
|
detail.coverage_area
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '非遗资产跨界合作深度',
|
label: '非遗资产跨界合作深度',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
value: formatEnumField('cooperationDepth', detail.cooperation_depth, detail.collaboration_type),
|
value: formatEnumField(
|
||||||
|
'cooperationDepth',
|
||||||
|
detail.cooperation_depth,
|
||||||
|
detail.collaboration_type
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '近12个月线下相关宣讲活动次数',
|
label: '近12个月线下相关宣讲活动次数',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
value: formatNumberValue(detail.offline_activities ?? detail.offline_teaching_count),
|
value: formatNumberValue(detail.offline_activities ?? detail.offline_teaching_count),
|
||||||
},
|
},
|
||||||
{ label: '线上相关宣传账号信息', type: 'list', value: formatPlatformAccounts(detail.platform_accounts) },
|
{
|
||||||
|
label: '线上相关宣传账号信息',
|
||||||
|
type: 'list',
|
||||||
|
value: formatPlatformAccounts(detail.platform_accounts),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'products',
|
key: 'products',
|
||||||
title: '非遗资产衍生商品信息',
|
title: '非遗资产衍生商品信息',
|
||||||
fields: [
|
fields: [
|
||||||
{ label: '代表产品近12个月销售数量', type: 'text', value: formatNumberValue(detail.sales_volume) },
|
{
|
||||||
|
label: '代表产品近12个月销售数量',
|
||||||
|
type: 'text',
|
||||||
|
value: formatNumberValue(detail.sales_volume),
|
||||||
|
},
|
||||||
{ label: '商品链接浏览量', type: 'text', value: formatNumberValue(detail.link_views) },
|
{ label: '商品链接浏览量', type: 'text', value: formatNumberValue(detail.link_views) },
|
||||||
{ label: '发行量', type: 'text', value: formatEnumField('circulation', detail.circulation, detail.scarcity_level) },
|
{
|
||||||
|
label: '发行量',
|
||||||
|
type: 'text',
|
||||||
|
value: formatEnumField('circulation', detail.circulation, detail.scarcity_level),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '最近一次市场活动时间',
|
label: '最近一次市场活动时间',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
value: formatEnumField('marketActivity', detail.last_market_activity, detail.market_activity_time),
|
value: formatEnumField(
|
||||||
|
'marketActivity',
|
||||||
|
detail.last_market_activity,
|
||||||
|
detail.market_activity_time
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '月交易额水平',
|
label: '月交易额水平',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
value: formatEnumField('monthlyTransaction', detail.monthly_transaction, detail.monthly_transaction_amount),
|
value: formatEnumField(
|
||||||
|
'monthlyTransaction',
|
||||||
|
detail.monthly_transaction,
|
||||||
|
detail.monthly_transaction_amount
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '近30天价格区间',
|
||||||
|
type: 'text',
|
||||||
|
value: formatPriceRange(detail.price_fluctuation),
|
||||||
},
|
},
|
||||||
{ label: '近30天价格区间', type: 'text', value: formatPriceRange(detail.price_fluctuation) },
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
// 为每个 section 生成 NDataTable 需要的 columns 和 data
|
// 为每个 section 生成 NDataTable 需要的 columns 和 data
|
||||||
return sections.map(section => {
|
return sections.map((section) => {
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: '字段名',
|
title: '字段名',
|
||||||
@ -196,13 +269,15 @@ const detailSections = computed(() => {
|
|||||||
align: 'center',
|
align: 'center',
|
||||||
fixed: 'left',
|
fixed: 'left',
|
||||||
},
|
},
|
||||||
...section.fields.map(field => ({
|
...section.fields.map((field) => ({
|
||||||
title: field.label,
|
title: field.label,
|
||||||
key: field.label,
|
key: field.label,
|
||||||
width: 200,
|
width: 200,
|
||||||
ellipsis: ['list', 'images'].includes(field.type) ? false : {
|
ellipsis: ['list', 'images'].includes(field.type)
|
||||||
|
? false
|
||||||
|
: {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
style: { maxWidth: '600px', maxHeight: '400px', overflow: 'auto' }
|
style: { maxWidth: '600px', maxHeight: '400px', overflow: 'auto' },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
render: (row) => {
|
render: (row) => {
|
||||||
@ -211,40 +286,48 @@ const detailSections = computed(() => {
|
|||||||
|
|
||||||
if (fieldData.type === 'list') {
|
if (fieldData.type === 'list') {
|
||||||
if (fieldData.value && fieldData.value.length) {
|
if (fieldData.value && fieldData.value.length) {
|
||||||
return h('div', { style: 'display: flex; flex-direction: column; gap: 4px;' },
|
return h(
|
||||||
fieldData.value.map(item => h('span', item))
|
'div',
|
||||||
|
{ style: 'display: flex; flex-direction: column; gap: 4px;' },
|
||||||
|
fieldData.value.map((item) => h('span', item))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return '-'
|
return '-'
|
||||||
} else if (fieldData.type === 'images') {
|
} else if (fieldData.type === 'images') {
|
||||||
if (fieldData.value && fieldData.value.length) {
|
if (fieldData.value && fieldData.value.length) {
|
||||||
const createImages = () => h(NImageGroup, {}, () =>
|
const createImages = () =>
|
||||||
fieldData.value.map(img =>
|
h(NImageGroup, {}, () =>
|
||||||
|
fieldData.value.map((img) =>
|
||||||
h(NImage, {
|
h(NImage, {
|
||||||
src: img,
|
src: img,
|
||||||
width: 72,
|
width: 72,
|
||||||
height: 48,
|
height: 48,
|
||||||
objectFit: 'cover',
|
objectFit: 'cover',
|
||||||
style: 'margin-right: 8px;'
|
style: 'margin-right: 8px;',
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return h(NPopover, {
|
return h(
|
||||||
|
NPopover,
|
||||||
|
{
|
||||||
trigger: 'hover',
|
trigger: 'hover',
|
||||||
displayDirective: 'show',
|
displayDirective: 'show',
|
||||||
keepAliveOnHover: true,
|
keepAliveOnHover: true,
|
||||||
style: { maxWidth: '600px', maxHeight: '400px', overflow: 'auto' }
|
style: { maxWidth: '600px', maxHeight: '400px', overflow: 'auto' },
|
||||||
}, {
|
},
|
||||||
trigger: () => h('div', { style: 'display: flex; overflow: hidden;' }, createImages()),
|
{
|
||||||
default: () => createImages()
|
trigger: () =>
|
||||||
})
|
h('div', { style: 'display: flex; overflow: hidden;' }, createImages()),
|
||||||
|
default: () => createImages(),
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return '-'
|
return '-'
|
||||||
} else {
|
} else {
|
||||||
return fieldData.value || '-'
|
return fieldData.value || '-'
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
})),
|
})),
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -268,18 +351,15 @@ const detailSections = computed(() => {
|
|||||||
|
|
||||||
const calcFlow = computed(() => props.detailData?.calculation_result?.flow || [])
|
const calcFlow = computed(() => props.detailData?.calculation_result?.flow || [])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const renderedFlowHtml = computed(() => {
|
const renderedFlowHtml = computed(() => {
|
||||||
return marked.parse(reportContent.value || mockReportMarkdown)
|
return marked.parse(reportContent.value || mockReportMarkdown)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
// 证书相关功能
|
// 证书相关功能
|
||||||
const handleUploadCertificate = () => {
|
const handleUploadCertificate = () => {
|
||||||
certificateModalMode.value = 'upload'
|
certificateModalMode.value = 'upload'
|
||||||
certificateData.value = {
|
certificateData.value = {
|
||||||
detailData: props.detailData
|
detailData: props.detailData,
|
||||||
}
|
}
|
||||||
certificateModalVisible.value = true
|
certificateModalVisible.value = true
|
||||||
}
|
}
|
||||||
@ -290,20 +370,23 @@ const handleViewCertificate = () => {
|
|||||||
const formatFiles = (urlData) => {
|
const formatFiles = (urlData) => {
|
||||||
if (!urlData) return []
|
if (!urlData) return []
|
||||||
// Handle string (single or comma-separated)
|
// Handle string (single or comma-separated)
|
||||||
const urls = typeof urlData === 'string' ? urlData.split(',') : (Array.isArray(urlData) ? urlData : [])
|
const urls =
|
||||||
|
typeof urlData === 'string' ? urlData.split(',') : Array.isArray(urlData) ? urlData : []
|
||||||
|
|
||||||
return urls.filter(u => u).map((url, index) => ({
|
return urls
|
||||||
|
.filter((u) => u)
|
||||||
|
.map((url, index) => ({
|
||||||
id: String(index),
|
id: String(index),
|
||||||
name: url.substring(url.lastIndexOf('/') + 1) || 'unknown',
|
name: url.substring(url.lastIndexOf('/') + 1) || 'unknown',
|
||||||
status: 'finished',
|
status: 'finished',
|
||||||
url: url
|
url: url,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
certificateData.value = {
|
certificateData.value = {
|
||||||
reportFiles: formatFiles(props.detailData?.report_url),
|
reportFiles: formatFiles(props.detailData?.report_url),
|
||||||
certificateFiles: formatFiles(props.detailData?.certificate_url),
|
certificateFiles: formatFiles(props.detailData?.certificate_url),
|
||||||
detailData: props.detailData
|
detailData: props.detailData,
|
||||||
}
|
}
|
||||||
certificateModalVisible.value = true
|
certificateModalVisible.value = true
|
||||||
}
|
}
|
||||||
@ -312,17 +395,17 @@ const handleCertificateConfirm = async (data) => {
|
|||||||
console.log('证书数据:', data)
|
console.log('证书数据:', data)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const certificateUrl = data.certificateFiles?.map(f => f.url).filter(Boolean) || []
|
const certificateUrl = data.certificateFiles?.map((f) => f.url).filter(Boolean) || []
|
||||||
const reportUrl = data.reportFiles?.map(f => f.url).filter(Boolean) || []
|
const reportUrl = data.reportFiles?.map((f) => f.url).filter(Boolean) || []
|
||||||
|
|
||||||
// 现在改为只能上传 1 张
|
// 现在改为只能上传 1 张
|
||||||
const payload = {
|
const payload = {
|
||||||
...props.detailData,
|
...props.detailData,
|
||||||
certificate_url: certificateUrl?.[0],
|
certificate_url: certificateUrl?.[0],
|
||||||
report_url: reportUrl?.[0],
|
report_url: reportUrl?.[0],
|
||||||
status: 'success'
|
status: 'success',
|
||||||
}
|
}
|
||||||
console.log("🔥🔥🔥🔥🔥🔥🔥 ~ handleCertificateConfirm ~ payload:", payload);
|
console.log('🔥🔥🔥🔥🔥🔥🔥 ~ handleCertificateConfirm ~ payload:', payload)
|
||||||
|
|
||||||
await api.updateValuation(payload)
|
await api.updateValuation(payload)
|
||||||
|
|
||||||
@ -359,12 +442,7 @@ const handleCertificateConfirm = async (data) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<NTabs
|
<NTabs v-model:value="activeDetailTab" type="line" size="large" class="audit-tabs">
|
||||||
v-model:value="activeDetailTab"
|
|
||||||
type="line"
|
|
||||||
size="large"
|
|
||||||
class="audit-tabs"
|
|
||||||
>
|
|
||||||
<NTabPane name="audit" tab="审核信息">
|
<NTabPane name="audit" tab="审核信息">
|
||||||
<NSpin :show="loading">
|
<NSpin :show="loading">
|
||||||
<div v-for="section in detailSections" :key="section.key" class="detail-section">
|
<div v-for="section in detailSections" :key="section.key" class="detail-section">
|
||||||
@ -395,19 +473,11 @@ const handleCertificateConfirm = async (data) => {
|
|||||||
|
|
||||||
<!-- 证书按钮 -->
|
<!-- 证书按钮 -->
|
||||||
<div class="certificate-actions">
|
<div class="certificate-actions">
|
||||||
<NButton
|
<NButton v-if="mode === 'approve'" type="primary" @click="handleUploadCertificate">
|
||||||
v-if="mode === 'approve'"
|
|
||||||
type="primary"
|
|
||||||
@click="handleUploadCertificate"
|
|
||||||
>
|
|
||||||
<TheIcon icon="mdi:upload" :size="16" class="mr-4" />
|
<TheIcon icon="mdi:upload" :size="16" class="mr-4" />
|
||||||
上传证书
|
上传证书
|
||||||
</NButton>
|
</NButton>
|
||||||
<NButton
|
<NButton v-else type="info" @click="handleViewCertificate">
|
||||||
v-else
|
|
||||||
type="info"
|
|
||||||
@click="handleViewCertificate"
|
|
||||||
>
|
|
||||||
<TheIcon icon="mdi:eye" :size="16" class="mr-4" />
|
<TheIcon icon="mdi:eye" :size="16" class="mr-4" />
|
||||||
查看证书
|
查看证书
|
||||||
</NButton>
|
</NButton>
|
||||||
@ -567,10 +637,23 @@ const handleCertificateConfirm = async (data) => {
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
line-height: 1.25;
|
line-height: 1.25;
|
||||||
}
|
}
|
||||||
.markdown-body :deep(h1) { font-size: 2em; border-bottom: 1px solid #eaecef; padding-bottom: .3em; }
|
.markdown-body :deep(h1) {
|
||||||
.markdown-body :deep(h2) { font-size: 1.5em; border-bottom: 1px solid #eaecef; padding-bottom: .3em; }
|
font-size: 2em;
|
||||||
.markdown-body :deep(h3) { font-size: 1.25em; }
|
border-bottom: 1px solid #eaecef;
|
||||||
.markdown-body :deep(p) { margin-top: 0; margin-bottom: 16px; }
|
padding-bottom: 0.3em;
|
||||||
|
}
|
||||||
|
.markdown-body :deep(h2) {
|
||||||
|
font-size: 1.5em;
|
||||||
|
border-bottom: 1px solid #eaecef;
|
||||||
|
padding-bottom: 0.3em;
|
||||||
|
}
|
||||||
|
.markdown-body :deep(h3) {
|
||||||
|
font-size: 1.25em;
|
||||||
|
}
|
||||||
|
.markdown-body :deep(p) {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
.markdown-body :deep(blockquote) {
|
.markdown-body :deep(blockquote) {
|
||||||
margin: 0 0 16px;
|
margin: 0 0 16px;
|
||||||
padding: 0 1em;
|
padding: 0 1em;
|
||||||
@ -592,7 +675,8 @@ const handleCertificateConfirm = async (data) => {
|
|||||||
.markdown-body :deep(table tr:nth-child(2n)) {
|
.markdown-body :deep(table tr:nth-child(2n)) {
|
||||||
background-color: #f6f8fa;
|
background-color: #f6f8fa;
|
||||||
}
|
}
|
||||||
.markdown-body :deep(table th), .markdown-body :deep(table td) {
|
.markdown-body :deep(table th),
|
||||||
|
.markdown-body :deep(table td) {
|
||||||
padding: 6px 13px;
|
padding: 6px 13px;
|
||||||
border: 1px solid #dfe2e5;
|
border: 1px solid #dfe2e5;
|
||||||
}
|
}
|
||||||
@ -767,6 +851,4 @@ const handleCertificateConfirm = async (data) => {
|
|||||||
color: #999;
|
color: #999;
|
||||||
padding: 40px 0;
|
padding: 40px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -148,7 +148,7 @@ export const formatPlatformAccounts = (accounts = {}) => {
|
|||||||
if (!info) return `${label}:-`
|
if (!info) return `${label}:-`
|
||||||
return `${label}:${info.account || '-'}(赞${formatNumberValue(info.likes)} / 评${formatNumberValue(
|
return `${label}:${info.account || '-'}(赞${formatNumberValue(info.likes)} / 评${formatNumberValue(
|
||||||
info.comments
|
info.comments
|
||||||
)} / 转${formatNumberValue(info.shares)})`
|
)} / 转${formatNumberValue(info.shares)}/ 七日浏览量${formatNumberValue(info.views)})`
|
||||||
})
|
})
|
||||||
return list.length ? list : ['暂无账号信息']
|
return list.length ? list : ['暂无账号信息']
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,11 @@ export default {
|
|||||||
getUserInfo: () => request.get('/base/userinfo'),
|
getUserInfo: () => request.get('/base/userinfo'),
|
||||||
getUserMenu: () => request.get('/base/usermenu'),
|
getUserMenu: () => request.get('/base/usermenu'),
|
||||||
getUserApi: () => request.get('/base/userapi'),
|
getUserApi: () => request.get('/base/userapi'),
|
||||||
|
deleteAccount: (data) => request.delete('/app-user/account', {
|
||||||
|
data: {
|
||||||
|
code: data.code // Body 中携带 code
|
||||||
|
}
|
||||||
|
}),
|
||||||
// 手机号
|
// 手机号
|
||||||
registerPhone: (data) => request.post('/app-user/register', data, { noNeedToken: true }),
|
registerPhone: (data) => request.post('/app-user/register', data, { noNeedToken: true }),
|
||||||
loginPhone: (data) => request.post('/app-user/login', data, { noNeedToken: true }),
|
loginPhone: (data) => request.post('/app-user/login', data, { noNeedToken: true }),
|
||||||
|
|||||||
BIN
web1/src/assets/icon/iconLogout.png
Normal file
BIN
web1/src/assets/icon/iconLogout.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
BIN
web1/src/assets/icon/评估完成.png
Normal file
BIN
web1/src/assets/icon/评估完成.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
@ -52,6 +52,14 @@ export const basicRoutes = [
|
|||||||
title: '抬头管理',
|
title: '抬头管理',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Logout',
|
||||||
|
path: 'logout',
|
||||||
|
component: () => import('@/views/user-center/components/Logout.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '账号注销',
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -185,11 +185,7 @@
|
|||||||
</n-form-item>
|
</n-form-item>
|
||||||
|
|
||||||
<div class="btn-container">
|
<div class="btn-container">
|
||||||
<button
|
<button class="primary-btn" :disabled="!isFormValid" @click="handleUploadSubmit">
|
||||||
class="primary-btn"
|
|
||||||
:disabled="!isFormValid"
|
|
||||||
@click="handleUploadSubmit"
|
|
||||||
>
|
|
||||||
确认上传
|
确认上传
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -295,11 +291,7 @@ const message = useMessage()
|
|||||||
|
|
||||||
// 计算表单是否有效
|
// 计算表单是否有效
|
||||||
const isFormValid = computed(() => {
|
const isFormValid = computed(() => {
|
||||||
return (
|
return uploadedFiles.value.length > 0 && !!formModel.invoiceHeader && !!formModel.invoiceType
|
||||||
uploadedFiles.value.length > 0 &&
|
|
||||||
!!formModel.invoiceHeader &&
|
|
||||||
!!formModel.invoiceType
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleUploadFinish = ({ file, event }) => {
|
const handleUploadFinish = ({ file, event }) => {
|
||||||
@ -384,10 +376,10 @@ defineExpose({
|
|||||||
}
|
}
|
||||||
|
|
||||||
.title-bar {
|
.title-bar {
|
||||||
width: 4px;
|
width: 6px;
|
||||||
height: 16px;
|
height: 24px;
|
||||||
background: #a30113;
|
background: #a30113;
|
||||||
border-radius: 2px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title-text {
|
.title-text {
|
||||||
|
|||||||
@ -315,10 +315,10 @@ const rowKey = (row) => row.id ?? row.name ?? row.company_name ?? row.taxId ?? r
|
|||||||
}
|
}
|
||||||
|
|
||||||
.title-bar {
|
.title-bar {
|
||||||
width: 4px;
|
width: 6px;
|
||||||
height: 16px;
|
height: 24px;
|
||||||
background: #a30113;
|
background: #a30113;
|
||||||
border-radius: 2px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title-text {
|
.title-text {
|
||||||
|
|||||||
368
web1/src/views/user-center/components/Logout.vue
Normal file
368
web1/src/views/user-center/components/Logout.vue
Normal file
@ -0,0 +1,368 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="header-left">
|
||||||
|
<div class="title-bar"></div>
|
||||||
|
<div class="title-text">账号注销</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="deleteAble" class="logout-container">
|
||||||
|
<!-- 手机号信息 -->
|
||||||
|
<div class="phone-info">当前绑定的手机号码为:{{ maskedPhone }}</div>
|
||||||
|
|
||||||
|
<!-- 验证码输入区域 -->
|
||||||
|
<div class="code-form">
|
||||||
|
<n-form :model="form" :rules="rules" ref="formRef" label-width="100px">
|
||||||
|
<n-form-item label="" prop="code" class="verify-code-item">
|
||||||
|
<!-- 验证码输入框 + 按钮 容器 -->
|
||||||
|
<div class="code-input-wrapper">
|
||||||
|
<n-input
|
||||||
|
v-model:value="form.code"
|
||||||
|
placeholder="手机验证码"
|
||||||
|
maxlength="6"
|
||||||
|
@input="handleCodeInput"
|
||||||
|
class="custom-code-input"
|
||||||
|
:bordered="false"
|
||||||
|
/>
|
||||||
|
<n-button
|
||||||
|
class="send-code-btn"
|
||||||
|
text
|
||||||
|
@click="handleSendCode"
|
||||||
|
:disabled="countDown > 0"
|
||||||
|
>
|
||||||
|
{{ countDown > 0 ? `${countDown}s后重新获取` : '获取验证码' }}
|
||||||
|
</n-button>
|
||||||
|
</div>
|
||||||
|
</n-form-item>
|
||||||
|
</n-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 注销按钮 -->
|
||||||
|
<n-button class="logout-btn" @click="handleLogout" :disabled="!isFormValid"> 确定 </n-button>
|
||||||
|
</div>
|
||||||
|
<div v-else class="logout-container" style="text-align: center">
|
||||||
|
<img src="@/assets/icon/评估完成.png" mode="scaleToFill" class="warn-icon" />
|
||||||
|
<p class="warn-title">无法注销</p>
|
||||||
|
<p class="warn-content">当前有未完成的估值记录/剩余估值次数,无法注销账号</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, watch, onUnmounted } from 'vue'
|
||||||
|
import { defineProps, defineEmits } from 'vue'
|
||||||
|
import { useMessage, useDialog } from 'naive-ui' // 引入 Naive UI 消息/弹窗
|
||||||
|
|
||||||
|
// 初始化 Naive UI 消息和弹窗
|
||||||
|
const message = useMessage()
|
||||||
|
const dialog = useDialog()
|
||||||
|
|
||||||
|
// 接收父组件传递的手机号
|
||||||
|
const props = defineProps({
|
||||||
|
userPhone: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
deleteAble: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// 定义事件
|
||||||
|
const emits = defineEmits(['send-code', 'confirm-logout'])
|
||||||
|
|
||||||
|
// 表单引用
|
||||||
|
const formRef = ref(null)
|
||||||
|
|
||||||
|
// 表单数据
|
||||||
|
const form = ref({
|
||||||
|
code: '',
|
||||||
|
})
|
||||||
|
// 倒计时
|
||||||
|
const countDown = ref(0)
|
||||||
|
let timer = null
|
||||||
|
|
||||||
|
// 手机号脱敏处理
|
||||||
|
const maskedPhone = computed(() => {
|
||||||
|
if (!props.userPhone) return ''
|
||||||
|
return `${props.userPhone.slice(0, 3)}****${props.userPhone.slice(7, 11)}`
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表单验证规则
|
||||||
|
const rules = ref({
|
||||||
|
code: [
|
||||||
|
{ required: true, message: '请输入验证码', trigger: 'blur' },
|
||||||
|
{ len: 6, message: '请输入6位验证码', trigger: 'blur' },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表单是否有效
|
||||||
|
const isFormValid = computed(() => {
|
||||||
|
return props.userPhone && form.value.code.length === 6
|
||||||
|
})
|
||||||
|
|
||||||
|
// 验证码输入限制
|
||||||
|
const handleCodeInput = () => {
|
||||||
|
if (form.value.code.length > 6) {
|
||||||
|
form.value.code = form.value.code.slice(0, 6)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送验证码
|
||||||
|
const handleSendCode = async () => {
|
||||||
|
if (!props.userPhone) {
|
||||||
|
message.warning('手机号不存在,无法发送验证码')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用父组件的发送验证码方法
|
||||||
|
const res = await emits('send-code', props.userPhone)
|
||||||
|
if (res) {
|
||||||
|
// 启动倒计时
|
||||||
|
countDown.value = 60
|
||||||
|
timer = setInterval(() => {
|
||||||
|
countDown.value--
|
||||||
|
if (countDown.value <= 0) {
|
||||||
|
clearInterval(timer)
|
||||||
|
timer = null
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
message.success('验证码发送成功')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交注销
|
||||||
|
const handleLogout = () => {
|
||||||
|
// 表单验证
|
||||||
|
formRef.value.validate((errors) => {
|
||||||
|
if (!errors) {
|
||||||
|
emits('confirm-logout', {
|
||||||
|
code: form.value.code,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
message.warning('请填写正确的6位验证码')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 页面卸载时清除定时器
|
||||||
|
watch(
|
||||||
|
() => countDown.value,
|
||||||
|
(val) => {
|
||||||
|
if (val <= 0 && timer) {
|
||||||
|
clearInterval(timer)
|
||||||
|
timer = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 组件卸载时清除定时器
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (timer) {
|
||||||
|
clearInterval(timer)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 根容器样式 */
|
||||||
|
.logout-container {
|
||||||
|
width: 600px;
|
||||||
|
margin: 50px auto;
|
||||||
|
padding: 30px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 标题样式 */
|
||||||
|
.logout-title {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #303133;
|
||||||
|
line-height: 12px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 手机号信息样式 */
|
||||||
|
.phone-info {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表单容器 */
|
||||||
|
.code-form {
|
||||||
|
margin-bottom: 40px;
|
||||||
|
margin-top: -20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 验证码表单项 */
|
||||||
|
.verify-code-item {
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 验证码输入框 + 按钮 容器(核心布局) */
|
||||||
|
.code-input-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 42px;
|
||||||
|
gap: 10px; /* 输入框和按钮间距 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 自定义验证码输入框样式 */
|
||||||
|
.custom-code-input {
|
||||||
|
flex: 1;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #e5e5e5;
|
||||||
|
font-size: 14px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
.warn-icon {
|
||||||
|
margin-top: 80px;
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
.warn-title {
|
||||||
|
margin-top: 20px;
|
||||||
|
font-size: 20px;
|
||||||
|
color: #000000;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.warn-content {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
/* 穿透修改 Naive UI 输入框样式 */
|
||||||
|
:deep(.custom-code-input .n-input-wrapper) {
|
||||||
|
height: 100%;
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
:deep(.custom-code-input .n-input__input-el) {
|
||||||
|
height: 100%;
|
||||||
|
line-height: 40px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 输入框 hover 状态 */
|
||||||
|
.custom-code-input:hover {
|
||||||
|
border-color: #a3011380; /* 半透明红 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 输入框 聚焦状态 */
|
||||||
|
:deep(.custom-code-input .n-input-wrapper:focus-within) {
|
||||||
|
border-color: #a30113;
|
||||||
|
box-shadow: 0 0 0 2px rgba(163, 1, 19, 0.1);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 输入框 错误状态 */
|
||||||
|
:deep(.n-form-item--error .custom-code-input) {
|
||||||
|
border-color: #d03050;
|
||||||
|
}
|
||||||
|
:deep(.n-form-item--error .custom-code-input .n-input-wrapper:focus-within) {
|
||||||
|
border-color: #d03050;
|
||||||
|
box-shadow: 0 0 0 2px rgba(208, 48, 80, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 验证码按钮样式 */
|
||||||
|
.send-code-btn {
|
||||||
|
height: 100%;
|
||||||
|
min-width: 120px;
|
||||||
|
padding: 0 15px;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #a30113;
|
||||||
|
font-size: 14px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
white-space: nowrap;
|
||||||
|
background: #a3011332;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按钮 hover(非禁用) */
|
||||||
|
.send-code-btn:not(:disabled):hover {
|
||||||
|
background: #a3011332;
|
||||||
|
color: #a30113;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按钮 点击态 */
|
||||||
|
.send-code-btn:not(:disabled):active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按钮 禁用态 */
|
||||||
|
.send-code-btn:disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 注销按钮样式 */
|
||||||
|
.logout-btn {
|
||||||
|
margin: auto;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 48px;
|
||||||
|
font-size: 16px;
|
||||||
|
width: 180px;
|
||||||
|
height: 40px;
|
||||||
|
background: #a30113;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 注销按钮 hover(非禁用) */
|
||||||
|
.logout-btn:not(:disabled):hover {
|
||||||
|
background: #a30113;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 注销按钮 禁用态 */
|
||||||
|
.logout-btn:disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
.title-bar {
|
||||||
|
width: 6px;
|
||||||
|
height: 24px;
|
||||||
|
background: #a30113;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-text {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
.header-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表单错误提示位置调整 */
|
||||||
|
:deep(.n-form-item-feedback-wrapper) {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
padding-top: 2px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.custom-code-input) {
|
||||||
|
/* Chrome/Safari/Edge */
|
||||||
|
input::-webkit-outer-spin-button,
|
||||||
|
input::-webkit-inner-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
/* Firefox */
|
||||||
|
input[type='number'] {
|
||||||
|
-moz-appearance: textfield;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(.n-button__content) {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -6,6 +6,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="user-phone">{{ userPhone || '18988880000' }}</div>
|
<div class="user-phone">{{ userPhone || '18988880000' }}</div>
|
||||||
<div class="valuation-count">剩余评估次数:{{ valuationCount }}</div>
|
<div class="valuation-count">剩余评估次数:{{ valuationCount }}</div>
|
||||||
|
|
||||||
|
<div class="logout-btn" @click="logout">退出登录</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="menu-list">
|
<div class="menu-list">
|
||||||
@ -29,24 +31,48 @@
|
|||||||
import iconHistory from '@/assets/icon/估值记录.png'
|
import iconHistory from '@/assets/icon/估值记录.png'
|
||||||
import iconTransfer from '@/assets/icon/对公转账.png'
|
import iconTransfer from '@/assets/icon/对公转账.png'
|
||||||
import iconInvoice from '@/assets/icon/抬头管理.png'
|
import iconInvoice from '@/assets/icon/抬头管理.png'
|
||||||
|
import iconLogout from '@/assets/icon/iconLogout.png'
|
||||||
|
import { useDialog } from 'naive-ui'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const dialog = useDialog()
|
||||||
defineProps({
|
defineProps({
|
||||||
userPhone: String,
|
userPhone: String,
|
||||||
valuationCount: Number,
|
valuationCount: Number,
|
||||||
currentMenu: String,
|
currentMenu: String,
|
||||||
menuList: Array
|
menuList: Array,
|
||||||
})
|
})
|
||||||
|
|
||||||
defineEmits(['menu-click'])
|
defineEmits(['menu-click'])
|
||||||
|
|
||||||
function getMenuIcon(id) {
|
function getMenuIcon(id) {
|
||||||
const iconMap = {
|
const iconMap = {
|
||||||
'history': iconHistory,
|
history: iconHistory,
|
||||||
'transfer': iconTransfer,
|
transfer: iconTransfer,
|
||||||
'invoice': iconInvoice
|
invoice: iconInvoice,
|
||||||
|
logout: iconLogout,
|
||||||
}
|
}
|
||||||
return iconMap[id]
|
return iconMap[id]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function logout() {
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
dialog.warning({
|
||||||
|
title: '提示',
|
||||||
|
content: '确认退出登录',
|
||||||
|
positiveText: '确认',
|
||||||
|
negativeText: '取消',
|
||||||
|
onPositiveClick: () => resolve(),
|
||||||
|
onNegativeClick: () => reject('cancel'),
|
||||||
|
onClose: () => reject('cancel'),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// 清除本地缓存
|
||||||
|
localStorage.removeItem('ACCESS_TOKEN')
|
||||||
|
// 跳转到登录页
|
||||||
|
router.push('/login')
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@ -117,12 +143,12 @@ function getMenuIcon(id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.menu-item:hover {
|
.menu-item:hover {
|
||||||
background: #F5F7FA;
|
background: #f5f7fa;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-item.active {
|
.menu-item.active {
|
||||||
background: #FFF0F0;
|
background: #fff0f0;
|
||||||
color: #A30113;
|
color: #a30113;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,6 +179,19 @@ function getMenuIcon(id) {
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.logout-btn {
|
||||||
|
width: 168px;
|
||||||
|
height: 32px;
|
||||||
|
background: #f5f5f5;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #909399;
|
||||||
|
line-height: 32px;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.sidebar-card {
|
.sidebar-card {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@ -217,10 +217,10 @@ onMounted(fetchHistory)
|
|||||||
}
|
}
|
||||||
|
|
||||||
.title-bar {
|
.title-bar {
|
||||||
width: 4px;
|
width: 6px;
|
||||||
height: 16px;
|
height: 24px;
|
||||||
background: #a30113;
|
background: #a30113;
|
||||||
border-radius: 2px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title-text {
|
.title-text {
|
||||||
|
|||||||
@ -16,23 +16,19 @@
|
|||||||
|
|
||||||
<!-- 右侧内容区 -->
|
<!-- 右侧内容区 -->
|
||||||
<div class="content-area">
|
<div class="content-area">
|
||||||
<!-- 返回按钮 (暂时注释) -->
|
|
||||||
<!-- <div class="back-button" @click="handleBackToHome">
|
|
||||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M15 18L9 12L15 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
|
||||||
<span>返回首页</span>
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<!-- 子路由视图 -->
|
<!-- 子路由视图 -->
|
||||||
<router-view
|
<router-view
|
||||||
:asset-list="assetList"
|
:asset-list="assetList"
|
||||||
:invoice-list="invoiceList"
|
:invoice-list="invoiceList"
|
||||||
|
:user-phone="userPhone"
|
||||||
|
:deleteAble="deleteAble"
|
||||||
@return-home="handleBackToHome"
|
@return-home="handleBackToHome"
|
||||||
@add-invoice="addInvoice"
|
@add-invoice="addInvoice"
|
||||||
@update-invoice="updateInvoice"
|
@update-invoice="updateInvoice"
|
||||||
@delete-invoice="deleteInvoice"
|
@delete-invoice="deleteInvoice"
|
||||||
@upload-submit="uploadSubmit"
|
@upload-submit="uploadSubmit"
|
||||||
|
@send-code="sendVerificationCode"
|
||||||
|
@confirm-logout="confirmAccountLogout"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -40,12 +36,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, h, watch, computed } from 'vue'
|
import { ref, onMounted, computed } from 'vue'
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
|
import { useMessage, useDialog } from 'naive-ui' // 引入 Naive UI 消息/弹窗
|
||||||
import api from '@/api'
|
import api from '@/api'
|
||||||
import AppHeader from '@/components/AppHeader.vue'
|
import AppHeader from '@/components/AppHeader.vue'
|
||||||
import UserSidebar from './components/UserSidebar.vue'
|
import UserSidebar from './components/UserSidebar.vue'
|
||||||
|
|
||||||
|
// 初始化 Naive UI 消息和弹窗实例
|
||||||
|
const message = useMessage()
|
||||||
|
const dialog = useDialog()
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
@ -55,12 +56,14 @@ const currentMenu = computed(() => {
|
|||||||
if (path.includes('/history')) return 'history'
|
if (path.includes('/history')) return 'history'
|
||||||
if (path.includes('/transfer')) return 'transfer'
|
if (path.includes('/transfer')) return 'transfer'
|
||||||
if (path.includes('/invoice')) return 'invoice'
|
if (path.includes('/invoice')) return 'invoice'
|
||||||
|
if (path.includes('/logout')) return 'logout' // 新增:注销菜单匹配
|
||||||
return 'history'
|
return 'history'
|
||||||
})
|
})
|
||||||
|
|
||||||
// 用户信息
|
// 用户信息
|
||||||
const userPhone = ref('')
|
const userPhone = ref('')
|
||||||
const valuationCount = ref(0)
|
const valuationCount = ref(0)
|
||||||
|
const deleteAble = ref(true)
|
||||||
|
|
||||||
// 资产列表
|
// 资产列表
|
||||||
const assetList = ref([])
|
const assetList = ref([])
|
||||||
@ -68,7 +71,7 @@ const assetList = ref([])
|
|||||||
// 开票列表
|
// 开票列表
|
||||||
const invoiceList = ref([])
|
const invoiceList = ref([])
|
||||||
|
|
||||||
// 菜单列表
|
// 菜单列表(新增注销菜单)
|
||||||
const menuList = ref([
|
const menuList = ref([
|
||||||
{
|
{
|
||||||
id: 'history',
|
id: 'history',
|
||||||
@ -82,6 +85,10 @@ const menuList = ref([
|
|||||||
id: 'invoice',
|
id: 'invoice',
|
||||||
label: '抬头管理',
|
label: '抬头管理',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'logout', // 新增:注销账号菜单
|
||||||
|
label: '账号注销',
|
||||||
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
// 返回首页
|
// 返回首页
|
||||||
@ -91,7 +98,6 @@ function handleBackToHome() {
|
|||||||
|
|
||||||
// 菜单点击
|
// 菜单点击
|
||||||
function handleMenuClick(menu) {
|
function handleMenuClick(menu) {
|
||||||
// 使用路由导航
|
|
||||||
router.push(`/user-center/${menu.id}`)
|
router.push(`/user-center/${menu.id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,9 +120,11 @@ async function loadData() {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载估值记录失败:', error)
|
console.error('加载估值记录失败:', error)
|
||||||
$message.error('加载估值记录失败,请稍后重试')
|
// 替换:ElMessage → message
|
||||||
|
message.error('加载估值记录失败,请稍后重试')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载发票抬头列表
|
// 加载发票抬头列表
|
||||||
async function loadInvoiceList() {
|
async function loadInvoiceList() {
|
||||||
try {
|
try {
|
||||||
@ -131,32 +139,38 @@ async function loadInvoiceList() {
|
|||||||
async function addInvoice(data) {
|
async function addInvoice(data) {
|
||||||
try {
|
try {
|
||||||
await api.addInvoiceHeaders(data)
|
await api.addInvoiceHeaders(data)
|
||||||
$message.success('添加成功')
|
// 替换:ElMessage → message
|
||||||
|
message.success('添加成功')
|
||||||
loadInvoiceList()
|
loadInvoiceList()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('新增发票抬头失败:', error)
|
console.error('新增发票抬头失败:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新发票抬头
|
// 更新发票抬头
|
||||||
async function updateInvoice(data) {
|
async function updateInvoice(data) {
|
||||||
try {
|
try {
|
||||||
await api.updateInvoiceHeaders(data)
|
await api.updateInvoiceHeaders(data)
|
||||||
$message.success('编辑成功')
|
// 替换:ElMessage → message
|
||||||
|
message.success('编辑成功')
|
||||||
loadInvoiceList()
|
loadInvoiceList()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('更新发票抬头失败:', error)
|
console.error('更新发票抬头失败:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除发票抬头
|
// 删除发票抬头
|
||||||
async function deleteInvoice(data) {
|
async function deleteInvoice(data) {
|
||||||
try {
|
try {
|
||||||
await api.deleteInvoiceHeaders(data.id)
|
await api.deleteInvoiceHeaders(data.id)
|
||||||
$message.success('删除成功')
|
// 替换:ElMessage → message
|
||||||
|
message.success('删除成功')
|
||||||
loadInvoiceList()
|
loadInvoiceList()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('删除发票抬头失败:', error)
|
console.error('删除发票抬头失败:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 上传对公转账凭证
|
// 上传对公转账凭证
|
||||||
async function uploadSubmit(data) {
|
async function uploadSubmit(data) {
|
||||||
try {
|
try {
|
||||||
@ -167,6 +181,53 @@ async function uploadSubmit(data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 新增:发送注销验证码
|
||||||
|
async function sendVerificationCode(phone) {
|
||||||
|
try {
|
||||||
|
await api.sendVerifyCode({ phone })
|
||||||
|
message.success('验证码已发送,请注意查收')
|
||||||
|
return true
|
||||||
|
} catch (error) {
|
||||||
|
console.error('发送验证码失败:', error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增:确认注销账号
|
||||||
|
async function confirmAccountLogout(formData) {
|
||||||
|
// 替换:ElMessageBox.confirm → dialog.warning
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
dialog.warning({
|
||||||
|
title: '提示',
|
||||||
|
content: '提交后账号将注销,不可撤回,确认注销账户?',
|
||||||
|
positiveText: '确认',
|
||||||
|
negativeText: '取消',
|
||||||
|
onPositiveClick: () => resolve(),
|
||||||
|
onNegativeClick: () => reject('cancel'),
|
||||||
|
onClose: () => reject('cancel'),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// 调用注销接口(传入手机号和验证码)
|
||||||
|
api
|
||||||
|
.deleteAccount({
|
||||||
|
code: formData.code,
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
console.log(res, '111111111111111')
|
||||||
|
// 替换:ElMessage → message
|
||||||
|
message.success('账号注销成功')
|
||||||
|
// 清除本地缓存
|
||||||
|
localStorage.removeItem('phone')
|
||||||
|
// 跳转到登录页
|
||||||
|
router.push('/login')
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err, '111111111111111')
|
||||||
|
deleteAble.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadData()
|
loadData()
|
||||||
loadInvoiceList()
|
loadInvoiceList()
|
||||||
@ -200,26 +261,7 @@ onMounted(() => {
|
|||||||
min-height: calc(100vh - 100px); /* Adjust based on header + margin */
|
min-height: calc(100vh - 100px); /* Adjust based on header + margin */
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden; /* Ensure rounded corners */
|
overflow: hidden; /* Ensure rounded corners */
|
||||||
}
|
padding: 20px; /* 新增:添加内边距 */
|
||||||
|
|
||||||
.back-button {
|
|
||||||
position: absolute;
|
|
||||||
top: 20px;
|
|
||||||
left: 20px;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 4px;
|
|
||||||
padding: 0;
|
|
||||||
color: #606266;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
font-size: 14px;
|
|
||||||
background: transparent;
|
|
||||||
z-index: 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-button:hover {
|
|
||||||
color: #a30113;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1200px) {
|
@media (max-width: 1200px) {
|
||||||
@ -232,9 +274,5 @@ onMounted(() => {
|
|||||||
.main-container {
|
.main-container {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
|
||||||
padding: 10px 20px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user